一、定义
- cpp允许同一个作用域中的某个函数(函数重载)和运算符(运算符重载类)指定多个定义;
- cpp允许在同一个作用域内可以声明几个功能不同的同名函数,形参列表(参数个数或类型或类型顺序)必须不同。
- 不能仅通过函数的返回值的不同来重载函数;
- 调用一个重载函数或重载运算符时,编译器通过把你使用的参数类型与定义中的参数类型进行比较,决定选用最合适的定义。选择合适的重载函数或重载运算符的过程,称为重载决策。
二、底层原理
1. 为什么c语言不支持函数重载?
- 程序在预编译结点都会经历预处理、编译、汇编、连接、生成可执行程序的过程,值得一提的是,在汇编过程中编译器会收集全局符号并生成全局符号表(将符号和其对应地址一一对应的表格成为符号表)。
- 每个文件里都会产生本文件的符号表,对于暂时找不到地址的函数(比如只是一个函数的声明),它的地址是一个没有意义的填充值:
在汇编的过程中我们生成了多个符号表,但最后只能合并为一个符号表,所以在连接过程中要对符号表进行合并。在合并过程中发现Add函数出现了两次,就涉及了重定位——Add函数有效的地址值就会作为Add函数最终的地址值。所以在C语言中两个重名函数的地址都是有效值,所以在重定位的时候就会产生冲突和歧义。
2. Cpp是如何支持函数重载的?
C语言符号表中出现了两个具有有效地址的同名函数名,所以发生了冲突,只要能解决函数名冲突的问题,相应的就可以实现函数重载的效果——Cpp对写入符号表的函数具有一个修正的过程:
在linux下观察反汇编的效果,发现函数名’f’已经被修正为 ‘_Z1fid’,我们可对Linux下的命名规则做如下总结:
_Z + 函数名长度 + 函数名 + 各个形参类型首字母的小写
(补充:int* 会修正为Pi)
举一反三,上面函数的函数名就会被修正为 _Z1fdi。既然函数名可以区分开来了,那么实现函数重载就不是问题了。
三、如何保证.c代码和.cpp代码之间的可移植性
1. Cpp程序调用C静态/动态库
C程序可以调用C的静态/动态库,C++程序可以调用C++的静态/动态库,那么能不能实现交叉调用呢?
这里最大的鸿沟在于C符号表的函数名就是原来的函数名,而C++中的则是经过修饰过的函数名,如果直接用C++程序去调用C的静态/动态库中的函数,是找不到对应函数的。所以在这里,extern C就派上用场。
有时候在C++工程中可能需要将某些函数按照C的风格来编译,在函数前加extern “C”,它的作用是将它所包含的函数采用C语言的规则进行编译链接,从而不会对函数名进行修正。
2. C程序调用Cpp静态/动态库
对.cpp文件动刀——此时我们在即将调用的C++库中加入这样一段条件;
- #ifdef __cplusplus
- #define EXTERN_C extern "C"
- #else
- #define EXTERN_C
- #endif
- EXTERN_C int add(int a, int b);
代码剖析:
__cpluscplus是C++项目默认设置的宏定义标识符,我们用它来判断是不是C++程序
在C++库中,EXTERN_C被解释成 extern “C” ,也就意味着C++库中函数名不会被修正
在C程序中,EXTERN_C被解释为空,不起到任何作用
在链接的时候,C程序和C++库中的函数名相同,在合并符号表时就不会出错
如果没有条件编译指令,只有 #define EXTERN_C extern “C” ,那么会因为C程序识别不了 extern “C” 而报错