好吧,承認是自己淺薄了
當被問起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 目標檔案
結果如下: