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 目標文件
結果如下: