C++函数重载
目录
一、函数重载:
二、C++支持函数重载的原理:
三、再谈函数重载:
一、函数重载:
C语言不允许函数同名,但是C++可以,需要构成函数重载。函数重载是函数参数类型不同,或者参数个数不同,或者参数类型顺序不同。
//函数重载
//参数类型不同
int Add(int x, int y = 10)
{
return x + y;
}
//参数个数不同
float Add(float x, float y, int z)
{
return x + y + z;
}
//参数类型顺序不同
double Add(double x, int y)
{
return x + y;
}
int main()
{
//匹配第一个函数
cout << Add(10) << endl;//打印结果为:20
//匹配第二个函数
cout << Add(3.14, 9.07, 10) << endl;//打印结果为:22.21
//匹配第三个函数
cout << Add(10.89, 30) << endl;//打印结果为:40.89
return 0;
}
函数返回值不同不能构成重载,两个命名空间的完全相同的函数不构成函数重载,只有在同一命名空间下才可能会构成函数重载。
二、C++支持函数重载的原理:
1)C语言不支持函数重载,而C++支持函数重载,这是因为与编译链接有关。
2)C语言的编译链接过程经过预处理、编译、汇编、链接四个阶段,C++的编译链接过程和C语言的大致相同。
预处理阶段:在预处理阶段,执行了头文件展开、去注释、宏替换、条件编译等,此过程处理的文件是.cpp文件,生成.i文件。
编译阶段:编译阶段执行了语法检查、语义检查等,此过程生成.s文件,也就是汇编代码(指令级代码)。
汇编阶段:汇编阶段将汇编代码转换成二进制机器码,形成符号表。在VS中,汇编阶段生成的目标文件是.obj文件,gcc中,生成的目标文件是.o文件。
链接阶段:生成.exe的可执行文件,该过程完成了合并段表、符号表的合并和重定位。
3)符号表:
在汇编阶段生成的符号表中,存在着变量或函数与地址的一一映射。C语言中是使用变量名或函数名充当变量或函数的地址,而C++中,会有变量名和函数名与它们的地址一一映射(即C语言使用名字充当地址,C++通过函数名找地址),尽管变量或函数名字相同,但它们的地址并不相同,每个地址都是唯一的,这就导致C语言不支持变量与函数重名,而C++可以存在重构函数。
C++有一个函数名修饰规则,每一个函数被赋予了一个新的函数名(修饰以后的函数名),当有同名的函数构成函数重载时,它们的新函数名并不相同,所以C++允许函数重名。
//函数名修饰规则
//这里只给出函数声明,没有函数定义,主要是为了观察VS中的函数名修饰规则
void func(int a, double b);
void func(double b, int a);
int main()
{
func(10, 3.14);
func(3.14, 10);
return 0;
}
这也就是为什么构成重载的函数,编译器同样知道该调用哪个函数,因为它们的新函数名有点不同,一个中间是HN,另一个是NH,也可以看出VS使用H代表int类型,N代表double类型。
那为什么函数只有声明没有定义的话,调用函数就会报错呢?因为函数没有定义就不会生成地址,使用新函数名去找这个地址的时候,就找不到。所以错误信息是无法解析的外部符号。
三、再谈函数重载:
为什么函数重载只有参数类型不同、参数顺序不同、参数个数不同这三种方式,上面的函数名修饰规则已经讲清楚了。
那么如果通过函数名修饰规则,设计出两个除了返回值类型不同,其余都相同的函数,它们可以构成函数重载吗?答案是否定的,因为对于这两个函数而言,确实可以创造出两个不同的新函数名,但是调用函数时候怎么办呢,两个调用的函数无论是传参的类型还是函数名都完全一样,编译器要怎么区分呢?除非把调用规则也修改了。