【C++】函数重载
在自然语言中一个词可以有多种含义,人们可以通过上下文来判断这个词的真是含义,即该词被重载了
比如:以前有一个笑话,国有两个体育项目大家根本不用看,也不用担心。一个是乒乓球,一个是男足。前者是“谁也赢不了!”,后者是“谁也赢不了!”
函数重载的概念
C++允许在同一个作用域声明几个功能类似的同名函数。这些函数要满足三个不同:参数的个数,参数的类型和参数的顺序,即构成函数重载。
我们来看一下构成重载的几个函数
int f(int a,int b);
double f(double a,double b);
void f(int a,double b);
void f(double a,int b);
void f();
需要注意的是:返回值相同与否并不影响是否重载,所以只有返回值不同的函数不构成重载。
下面来区分一下两队比较容易出错的函数重载
void f(int a);
void f(int a=0);
这两个函数的区别是缺省值不同,而函数重载的定义是三个不同,关注的是形参的类型,与缺省值无关,所以不构成重载。
void f();
void f(int a=0);
这两个函数就构成函数重载,因为他们的参数个数不同,第一个无参,第二个有一个参数,但是当我们在使用这两个函数时,如果我们没有给函数传值,那么这时调用两个函数都是可以的,这就出现了歧义,编译器也不能确定调用哪一个,所以我们不能这样写重载函数。
重载的原理
C++是如何支持函数重载的呢?C语言又为什么不能支持函数重载呢?
首先我们要了解一下编译的过程
编译分为4步,我大致的梳理一下
1.预处理–头文件的展开,宏替换,条件编译,去掉注释等。生成(.i)文件
2.编译–检查语法,生成汇编代码。生成(.s)文件
3.汇编–把汇编代码转换成二进制的机器码。生成(.o)文件
4.链接-把二进制的机器码和在一起。生成(.out)文件
要理解函数重载的原理,我们就可以看一下生成的汇编代码,可以帮助我们理解计算机是如何实现函数的调用的。
我们先写两个重载函数
void f()
{
cout<<"f()"<<endl;
}
void f(int a)
{
cout<<"f(a)"<<endl;
}
int main()
{
f();
f(1);
return 0;
}
在VS编译器下,我们可以在调试时点鼠标右键—>进入反汇编 看当前代码对应的汇编代码
这是我的编译器上的汇编代码
调用函数的操作在汇编代码下变成了call f(一个地址)。
现在我在通过这个地址去看看这个地址是什么意思。
这个地址是一个jmp指令的地址,这个jmp指令又跳到了另一个地址上,我们再通过后面的地址去看看这是什么
到了这里大家应该就发现了,这不就是我们函数定义的地方吗。
那我们就可以发现,在我们调用函数的时候,首先要拿到一个jmp指令的地址(call (地址)),然后再jmp到函数的地址上(jmp (地址))。
问题就出在了call找f()的地址上,在预处理时,头文件中一般只有函数的声明,没有定义,那在汇编中的地址就是这样的状态call f(?)(这个问号只是表示这个地址是未知的,并不代表汇编代码这里真的是’?’),在C语言中,两个调用函数都会变成call f(?),也就是说,C语言识别函数的方法是通过函数名这种方法来识别的,非常简单。
而代码在从.c文件到.i文件再到.o文件时,之前的每一个.c文件都会生成一个对应的.o文件。
在每个对应的.o文件下都会生成一个符号表,符号表里存储的内容之一就是这个里面函数的地址。
当我test.o里的两个函数想要去func.o的符号表里去找函数的地址时,我们发现,它俩的名字是一样的,因为他们是同名函数,而C语言又是依靠函数名在符号表里寻找函数地址的,所以就会出现有两个函数名一样的地址,虽然地址是两个不同函数的地址,但是区分他们的符号是相同的,那就会造成链接错误,这也就是C语言无法支持函数重载的原因。
总结一下C语言不支持重载的原因:
(1)编译生成的两个重载函数的函数名相同,在func.o的符号表中存在歧义和冲突。
(2)链接的时候也存在歧义和冲突,因为他们都是使用函数名去表示和查找的,而重载函数的函数名相同。
而C++又是这么支持了函数重载的呢?
C++的目标文件符号表不是直接用函数名来表示和查找函数了
(1)C++修改了函数名的修饰规则(不同的编译器修饰规则不同),使修饰符号不光与函数名有关,还与参数有关。
(2)有了函数名修饰规则,那么只要参数不同,func.o的符号表里的重载函数就不存在冲突了。
(3)在链接时,test.o里调用那两个函数时他们查找的地址也是明确的。**
到这里我们就理解了为什么C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。
另外我们也理解了,为什么函数重载要求参数不同,因为C++正是通过在修饰函数名时加上了参数的影响做到区分同名函数的。
函数名的修饰规则比较复杂,而且在不同的编译器下又不一样,大家可以去看看别的大佬在这方面的文章,这里就不赘述了。
以上就是本篇的全部内容。