C++ -函数重载-详解

博客主页:【夜泉_ly

本文专栏:【C++

欢迎点赞👍收藏⭐关注❤️

C++ -函数重载-详解

1.是什么

如果在百度中搜索重载 这个词,会得到以下结果:

不怎么容易理解吧🤣。

其实,可以简单的把重载理解为同名,而函数重载就是定义同名函数。

例如:

cpp 复制代码
void Add(int a, int b);
void Add(double a, double b);

这在C语言中肯定会报错,但C++支持这样的操作,并且在调用时会根据传入的参数类型自动匹配对应的函数。

2.怎么用

函数重载不能支持所有同名函数,首先,参数列表需要不同,主要分三方面:

  • 参数个数不同
  • 参数类型不同
  • 类型顺序不同

这意味着,如果函数名相同、参数列表相同、但是返回值不同 ,不构成重载!例如:

cpp 复制代码
int func(int a, int b);
double func(int a, int b);

2.1示例

cpp 复制代码
void func();	 //无参数
void func(int a);//单参数

构成重载------参数个数不同。


cpp 复制代码
void func(int a, int b);//参数顺序不同
void func(int b, int a);//参数顺序不同

有歧义,不构成重载。

报错信息如下:


cpp 复制代码
void func(int  a, int b);//第一个参数为整数
void func(char a,int b);//第一个参数为字符

构成重载------参数类型不同。


cpp 复制代码
void func(int a, char b);//第一个参数为整数
void func(char a,int b);//第一个参数为字符

构成重载------类型顺序不同。


这时有人想到了缺省参数💡,于是出现了这种比较特殊的情况:

cpp 复制代码
void func();
void func(int a = 0);

是否构成重载?
构成重载!符合重载函数的定义。

可以在上图的右下角看见,程序正常退出。

那么这样写有没有问题?
有问题!如果写出下面这句代码,则会引发歧义:

cpp 复制代码
func();

3.原理

这里有两个问题:

  • 为什么C语言不支持重载,C++支持重载?
  • C++是怎么支持重载的?

想要解决这些问题,需要了解编译链接过程 ,以及函数名修饰规则

自动识别类型-函数重载。

3.1C/C++编译链接过程

这里只是简单提两句,因为更详细的内容我也不知道:

  1. 预处理:
    头文件展开 / 宏替换 / 条件编译 / 去掉注释......(.h/.c/.cpp文件变为.i文件)

  2. 编译:
    检查语法 / 生成汇编代码(.i文件变为.s文件)

    上面那个缺省参数的特殊情况在编译时不会报错,就是因为语法没错。

  3. 汇编:

    将汇编代码转化为二进制的机器码(.s文件变为.o文件)

  4. 链接:
    生成符号表 / 将目标文件(.o-->object)链接 成可执行程序(.exe/a.out

3.2函数名修饰规则

C++ 编译器在编译过程中会对函数名进行修饰,以便区分不同的重载函数。修饰规则通常包括以下内容:

  • 参数类型:函数参数的类型信息。
  • 参数数量:函数参数的数量。
  • 返回类型:函数的返回类型信息。

在不同环境下,函数名修饰规则也不同,但目的都是将同名变为不同名。

3.3过程

二进制机器码与汇编代码是相互转换的关系,也就是说,如果想查看程序的底层实现,可以通过查看汇编代码来获取更直观的信息。

我在这里又建了三个文件:
func.h:

cpp 复制代码
#pragma once
#include <stdio.h>
void func(int a, int b);
void func(double a, double b);

func.c:

cpp 复制代码
#include "func.h"
void func(int a, int b)
{
	printf("void func(int a, int b)\n");
}
void func(double a, double b)
{
	printf("void func(double a, double b)\n");
}

test.c:

cpp 复制代码
#include "func.h"
int main()
{
	func(1, 2);
	func(1.0, 2.0);
	return 0;
}

1.调用函数的过程

在开始调试的时候转到反汇编,会看见:

这里可以看见,在每个函数调用语句下都有一条"call指令+地址":

这里重点关注第一个call指令,在调试到那里时,会跳转到jump指令,而这个jump指令的地址就是刚刚call指令后跟的地址:

而这个jump指令,会根据它后面跟的地址,真正跳转到函数的位置:

2.编译阶段的函数调用

在编译过程中,函数调用会被转化为 call 指令,后面跟着函数名(函数的地址),类似刚刚看见的:

当函数的定义和声明分离时,编译阶段无法立即获取函数的实际地址,因为此时只包含了(.h),即编译器只看到声明,而函数的定义在其他文件中。

在这种情况下,编译器能通过声明完成编译,但不会生成最终的地址。(我只会用VS2022,而且不知道怎么调出这种效果😅)

在链接阶段,链接器才会将调用语句与实际的函数定义关联起来,找到函数的真正地址。

编译错误与链接错误:

  • 因为编译器在看到声明时只知道函数存在,但没有具体的实现细节,因此只要声明正确,编译可以顺利通过。
  • 但是,链接阶段如果找不到函数的定义,就会发生链接错误。

当然,当函数的定义与声明在同一个源文件中时,编译器可以直接找到函数的地址,因为定义已经存在,编译器能够在编译阶段就处理地址问题。


回到函数重载:

编译阶段 ,C++编译器会通过函数名修饰为每个重载函数生成唯一的符号,从而确保编译器能够区分同名但参数列表不同的重载函数。

而C语言不会进行函数名修饰,因此遇到同名函数会编译失败。

这就解释了为什么C语言不支持重载,C++支持重载?和C++是怎么支持重载的?

总结

函数重载是 C++ 中一项重要的特性,它提高了代码的灵活性和可读性。

通过函数名修饰规则,C++ 在编译过程中实现了自动类型匹配,避免了调用时的歧义。

在实际编程中,合理使用函数重载可以极大地提高编程效率和代码质量。


希望本篇文章对你有所帮助!并激发你进一步探索编程的兴趣!

本人仅是个C语言初学者,如果你有任何疑问或建议,欢迎随时留言讨论!让我们一起学习,共同进步!

相关推荐
Dontla10 分钟前
Rust泛型系统类型推导原理(Rust类型推导、泛型类型推导、泛型推导)为什么在某些情况必须手动添加泛型特征约束?(泛型trait约束)
开发语言·算法·rust
tumu_C38 分钟前
C++模板特化实战:在使用开源库boost::geometry::index::rtree时,用特化来让其支持自己的数据类型
c++·开源
杜若南星1 小时前
保研考研机试攻略(满分篇):第二章——满分之路上(1)
数据结构·c++·经验分享·笔记·考研·算法·贪心算法
Neophyte06081 小时前
C++算法练习-day40——617.合并二叉树
开发语言·c++·算法
慕容复之巅1 小时前
基于MATLAB的条形码的识别图像处理报告
开发语言·图像处理·matlab
云空1 小时前
《InsCode AI IDE:编程新时代的引领者》
java·javascript·c++·ide·人工智能·python·php
zqzgng1 小时前
Python 数据可视化pilot
开发语言·python·信息可视化
写bug的小屁孩1 小时前
websocket初始化
服务器·开发语言·网络·c++·websocket·网络协议·qt creator
Dr_eamboat1 小时前
【Java】枚举类映射
java·开发语言·python
代码小鑫2 小时前
A031-基于SpringBoot的健身房管理系统设计与实现
java·开发语言·数据库·spring boot·后端