C++相對於C語言而言支持函數重載是其極大的一個特點,相信在使用C語言的時候大家如果要寫一個實現兩個整型數據相加的函數還要寫一個浮點型數據相加的函數,那么這兩個函數的名字絕對不可以一樣,這樣無疑在我們使用這個函數的時候增加了復雜性,但是在C++中我們卻可以很好的解決這個問題,因為在C++中函數是支持重載的也就是說兩個函數的函數名可以一樣,這樣並不會出現函數名重定義的問題,但是我們在使用的時候也要遵守一些規定,這些規定我們會在接下來的討論中提到,下面我們就來分析在C++中函數是如何實現函數的重載的。
在這里我們用C語言和C++分別寫兩個函數,通過函數的符號表來觀察函數名在經過編譯之后究竟是什么形式的
下面就是我們的測試代碼:
1 #include<iostream> 2 3 using namespace std; 4 5 6 7 int Add(int x, int y) 8 9 { 10 11 int z = 0; 12 13 z = x + y; 14 15 return z; 16 17 } 18 19 20 21 double Add(double x, double y) 22 23 { 24 25 double z = 0; 26 27 z = x + y; 28 29 return z; 30 31 } 32 33 34 35 int main() 36 37 { 38 39 cout<<Add(1,3)<<endl; 40 41 cout<<Add(1.5,3.5)<<endl; 42 43 return 0; 44 45 }
在VS2008的編譯環境下:
我們生成.map文件,然后可以查看函數在經過編譯之后的函數名稱為下圖所示:
不難發現上圖中函數命名的一些規律(當然這個規律只是片面的針對於VS2008編譯環境):
1.以“?”開始和以”@Z”結尾
2.函數的名稱緊接“?”之后
3.在函數明德后面分別是函數返回值類型修飾符、參數列表中的參數的類型修飾符
下面我們把這個相同的函數改為c語言的代碼
代碼如下:
1 //#include<iostream> 2 3 //using namespace std; 4 5 #include<stdio.h> 6 7 8 9 10 11 int Add(int x, int y) 12 13 { 14 15 int z = 0; 16 17 z = x + y; 18 19 return z; 20 21 } 22 23 24 25 double Add(double x, double y) 26 27 { 28 29 double z = 0; 30 31 z = x + y; 32 33 return z; 34 35 } 36 37 38 39 int main() 40 41 { 42 43 //cout<<Add(1,3)<<endl; 44 45 //cout<<Add(1.5,3.5)<<endl; 46 47 return 0; 48 49 }
這時我們編譯的話就會出現錯誤:
這里告訴我們函數名出現重定義
那么這是為什么呢?
這時我們注釋掉一個函數然后編譯后查看.map文件,查看函數重命名之后的名稱
這里我們可以發現函數在編譯之后重命名的名稱僅僅只是在函數名稱的前面加上了一個"_"(下划線),這樣我們就不難分析了,C和C++編譯的時候對函數的重命名機制是完全不一樣的
1.C語言中僅僅只是在函數的名稱的前面加上了"_"(下划線)
2.C++有自己的命名修飾規則,他會根據函數的參數列表中變量的類型等進行相應的類型修飾
雖然C++支持函數的重載但是我們在使用的時候也要注意以下幾點:
1.函數的重載只是出現在同一作用域,例如假如兩個工程里的函數名稱相同,但是他們也不是函數的重載
2.函數名相同,函數的參數列表不同,返回值可同可不同,為什么函數返回值可同可不同呢?
這是因為在不同的編譯環境下對於函數名稱的修飾並不是相同的,下面就是在Linux環境下函數編譯后重命名的形式:
仔細觀察不難發現在Linux環境下的函數重命名的一些規則:
1.以“_Z”z作為開頭,緊隨其后的數字是函數名稱的單詞的個數
2.函數的名稱后面有函數的參數列表中參數的類型修飾符,i是int型,d是double型
通過以上的闡述相信大家可以對C++中為什么可以實現函數重載有了清晰的認識,那么我們也就不難回答為什么在C++中調用被C編譯過后的函數應該在前面加上 extern "C" 聲明了。
這是因為我們當前是處於c++語言環境,這個時候我們如果不指定要調用的那個函數是用C語言編譯的函數,那么當前在C++文件中編譯時就會報錯說是該函數是一個無法解析的外部符號,因為在編譯運行的時候我們當前的程序會從符號表中去找相應的函數名,可是C++和C編譯后生成的符號表中函數的名是不同的,那么這個函數也就是一個無法解析的外部符號了,但是當你用extern “C” 指明該函數是用C語言編譯的函數,那么當前代碼在編譯運行的時候就會從用C語言編譯的那個符號表文件中去查找相應的函數名,這樣整個程序的編譯運行費也就沒有問題了。