C++重载底层原理
好吧,承认是自己浅薄了
当被问起C++重载时,嘴角不自觉的微微上扬,然后脱口而出,C++重载的原则:
- 函数名相同,函数参数列表不同(类型、个数、顺序)
- 匹配原则1:严格匹配,找到再调用
- 匹配原则2:通过隐式类型转换寻求一个匹配,找到则调用
- 注:返回类型不构成重载条件
C++编译时多态也是由重载函数来实现的,那既然扯到多态了,顺便也把运行时多态(虚函数)相关的东西简单了说了下
结果谁成想,反手就问了C++重载的底层实现原理是怎样的?
这。。。瞬间蒙蔽
或者问:为什么C没有重载,C++有重载
------不华丽的分割线------
先说结论:
C++针对函数名有经过一种叫Name Mangling的特殊处理,网上很多都是翻译成了命名倾轧
成员函数的函数名会经过Name Mangling处理,得到一个程序中独一无二的词汇。
- Name Mangling对成员变量的处理,一般会在变量名称前加上类名称,形成独一无二的命名。
举例:
class Bar{public: int ival;...}
其中的ival
有可能变成:
ival_3Bar
PS:这个结果,可能会因为编译器的编码方法不同而不同。
- 针对成员函数,为让它们独一无二,唯有再加上它们的参数列表
举例:
class Point{
public:
void x(float newX,int newY);
void x(int newY, float newX);
float x();
...
}
它可能转换为:
class Point{
public:
void x_5PointFfi(float newX, int newY);
void x_5PointFif(int newY, float newX);
float x_5PointFv();
...
}
这也就解释了为什么C++重载对参数类型、顺序、数量作为重载的原则。
至于C为什么不能重载,那是因为编译器只是对函数名做了独一无二的命名处理,并没有带上参数相关的信息。
另:
如果声明了extern "C"
,就会禁止命名倾轧name mangling
的效果。
------不华丽的分割线------
一个完整的C++编译过程(例如g++ a.cpp生成可执行文件),总共包含以下四个过程:
- 编译预处理,也称预编译,可以使用命令
g++ -E
执行- 编译,可以使用
g++ -S
执行- 汇编,可以使用
as
或者g++ -c
执行- 链接,可以使用
g++ xxx.o xxx.so xxx.a
执行
# -E 编译器对文件进行预处理
g++ -E test.cpp -o test.i //i文件
# -S编译器告诉g++再为c++代码产生汇编语言后停止编译
g++ -S test.i -o test.s
# -c 选项告诉g++仅把源代码编译为机器语言的目标代码
g++ -c test.s -o test.o (-c小写)
# -0 产生可执行文件名
g++ test.0 -o test
写代码来看下:
通过g++ -c
会将源代码编译成机器语言的目标代码,然后使用objdump -t 目标文件
将二进制文件进行反汇编,具体如下:
其中,_Z
是规定前缀,4
是函数名的字符个数,i
是参数列表类型i的首字母
C++也提供了命名反倾轧
1.将名字改编转化成函数名
使用c++filt
命令可以很容易把名字改编转换成函数名
c++filt _Z4funci
- 查看反倾轧的符号表
有两种方式:
-
nm -C 目标文件
-
objdump -t -C 目标文件
结果如下: