目录
概念:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题.
定义重载函数:
1.参数类型不同
int Add(int left, int right)
{
cout << "int Add(int left, int right)" << endl;
return left + right;
}
double Add(double left, double right)
{
cout << "double Add(double left, double right)" << endl;
return left + right;
}
2.参数个数不同
void f()
{
cout << "f()" << endl;
}
void f(int a)
{
cout << "f(int a)" << endl;
}
3.参数类型顺序不同
3、参数类型顺序不同
void f(int a, char b)
{
cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
cout << "f(char b, int a)" << endl;
}
注意:
1.返回值不同不能够构成重载.
2.参考下面代码,两个Function()函数构成重载,但是在调用时由于都不需要传参,编译器在编译时构成歧义,到底该调用哪一个?
函数重载原理:
两个知识点:
1.文件的编译过程
2.名字修饰
文件编译过程
一个C文件要运行起来,大致分为这几个阶段:
1.预编译(文件预处理/宏替换/头文件展开/条件编译/去掉注释)
2.编译(语法分析,词法分析,语义分析,符号汇总,生成汇编代码)
3.汇编(形成符号表,生成二进制指令)
4.链接(合并段表,符号表合并以及符号重定位),生成可执行程序
#如果不了解就看
而C程序的函数的调用过程:
你需要从这里面读取的信息是:main()将函数调用翻译成汇编就是call .....(一个地址),该地址对应着jmp指令,而由jmp指令跳转到相应的执行函数指令的地址处,压栈操作结束后,就是函数体内的指令执行.
在c文件中,不论是函数名还是变量名,在执行的时候起始就是一串地址,其大致过程是在编译完成后生成一个符号表,类似下面:
如果有多个c文件,经过编译后会生成多个多个后缀为.i的文件,每个文件对应各自的符号表,我们经常在源文件中对一个函数声明,而在另一个文件中定义:
声明就是在符号汇总的时候发挥作用,生成一个诸如下面的符号表:
只是把函数名记录在符号表中,而不去添加其相应的地址,因为我在本文件中没有定义该函数体,该函数体存在于其他的文件中,其相应地址的填写发生在链接过程,即符号表汇总.
名字修饰:
对文件的编译过程有一个大致的了解,这里需要说明,在你编写多个函数名相同的函数时,在编译的符号汇总阶段,其实编译器有一个小动作,就是根据函数的参数列表,对你的同名函数重新命名,我们在liunx下观察:
先编写一段C++代码:
保存在test.c文件中.
上述指令用于编译test.c文件,-o指令指定编译后的文件名:
testcpp就是test.c编译后的文件,经过了预处理和编译.
以上指令用于打开我们编译好的代码,我们找到相应的ADD函数:
编写C代码,看看编译后有什么不同:
发现没有?C++编译的时候对函数名根据参数列表进行了修饰,原本的ADD变成了ADDii,和ADDdd,而C中编译后没有什么变化.所以这才是为什么C++可以支持函数重载.
总结:
1.列表参数不同的同名函数构成重载,这里列表参数不同即:形参个数不同,形参类型不同,形参类型的顺序不同.
2.c代码编译可执行文件经过了预编译,编译,汇编,链接几个过程:
预编译阶段去掉注释,头文件展开,宏替换,条件编译.
编译阶段进行语法分析,语义分析,词法分析,符号汇总,转换成汇编代码
汇编阶段将汇编代码转化为二进制代码, 生成符号表
链接阶段合并段表和符号表,并对符号表进行汇总.
3.C++之所以能进行函数重载是因为在编译阶段对函数名进行了名字修饰.