zl程序教程

您现在的位置是:首页 >  其他

当前栏目

C++重载底层原理

2023-03-20 15:01:06 时间

好吧,承认是自己浅薄了
当被问起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
写代码来看下:
image

通过g++ -c会将源代码编译成机器语言的目标代码,然后使用objdump -t 目标文件将二进制文件进行反汇编,具体如下:
image
其中,_Z是规定前缀,4是函数名的字符个数,i是参数列表类型i的首字母

C++也提供了命名反倾轧
1.将名字改编转化成函数名
使用c++filt命令可以很容易把名字改编转换成函数名

c++filt _Z4funci
  1. 查看反倾轧的符号表
    有两种方式:
  • nm -C 目标文件

  • objdump -t -C 目标文件
    结果如下:
    image

可参考:
绝对强大的三个linux指令: ar, nm, objdump