C++中重載函數詳解


函數的重載詳解

什么時函數重載:

  函數重載是指在同一作用域內,可以有一組具有相同函數名,不同參數列表的函數,這組函數被稱為重載函數。重載函數通常用來命名一組功能相似的函數,這樣做減少了函數名的數量,避免了名字空間的污染,對於程序的可讀性有很大的好處。

    1.是函數的一種特殊情況,C++允許在同一作用域中聲明幾個功能類似的同名函數,這些同名函數的形參列表(參數“個數” 或 “類型” 或 “順序”)必須不同,常用來處理實現功能類似數據類型不同的問題(這也是C++與C語言的最重要區別)

 

 1 int Add(int left, int right) 
 2 { 3 return left+right; 4 } 5 6 double Add(double left, double right) 7 { 8 return left+right; 9 } 10 11 long Add(long left, long right) 12 { 13 return left+right; 14 } 15 16 int main() 17 { 18 Add(10, 20); 19 Add(10.0, 20.0); 20 Add(10L, 20L); 21 22 return 0; 23 }

例如這里定義了三個Add函數,傳入的參數與順序都相同,都是 left/ right ,但參數的類型卻不同,有 int型,long型, double型,所以可以依靠這些類型的不同來區分要調運哪個函數。

在這里要特別注意:這里的參數必須是參數的 個數 或 類型 或 順序不同,如果是返回值不同,則不屬於函數重載,如:Add前的int, long, double。

下面就介紹一下在編譯時具體的名字修飾規則:

2.名字修飾

  Name Mangling是一種在編譯過程中,將函數、變量的名稱重新改編的機制,簡單來說就是編譯器為了區分各 個函數,將函數通過某種算法,重新修飾為一個全局唯一的名稱。
  C語言的名字修飾規則非常簡單,只是在函數名字前面添加了下划線。比如,對於以下代碼,在后鏈接時就 會出錯:

1 int Add(int left, int right);
2  
3 int main() 4 { 5 Add(1, 2); 6 return 0; 7 }

編譯器報錯:error LNK2019: 無法解析的外部符號 _Add,該符號在函數 _main 中被引用。

上述Add函數只給了聲明沒有給定義,因此在鏈接時就會報錯,提示:在main函數中引用的Add函數找不到函數體。從報錯結果中可以看到,C語言只是簡單的在函數名前添加下划線。因此當工程中存在相同函數名的函數時,就會產生沖突。
由於C++要支持函數重載,命名空間等,使得其修飾規則比較復雜,不同編譯器在底層的實現方式可能都有差異

1 int Add(int left, int right); 
2 double Add(double left, double right); 3 4 int main() 5 { 6 Add(1, 2); 7 Add(1.0, 2.0); 8 return 0; 9 }

在vs下,對上述代碼進行編譯鏈接,后編譯器報錯:
error LNK2019: 無法解析的外部符號 "double cdecl Add(double,double)" (?Add@@YANNN@Z) error LNK2019: 無法解析的外部符號 "int __cdecl Add(int,int)" (?Add@@YAHHH@Z)

通過上述錯誤可以看出,編譯器實際在底層使用的不是Add名字,而是被重新修飾過的一個比較復雜的名字, 被重新修飾后的名字中包含了:函數的名字以及參數類型。這就是為什么函數重載中幾個同名函數要求其參數列表不同的原因。只要參數列表不同,編譯器在編譯時通過對函數名字進行重新修飾,將參數類型包含在終的名字中,就可保證名字在底層的全局唯一性。

比較c/c++中的名字修飾:

  a、C編譯時函數名修飾約定規則:

  __stdcall調用約定在輸出函數名前加上一個下划線前綴,后面加上一個“@”符號和其參數的字節數,

  格式為_functionname@number。

  __cdecl調用約定僅在輸出函數名前加上一個下划線前綴,格式為_functionname。

  __fastcall調用約定在輸出函數名前加上一個“@”符號,后面也是一個“@”符號和其參數的字節數,

  格式為@functionname@number。

  它們均不改變輸出函數名中的字符大小寫,這和PASCAL調用約定不同,PASCAL約定輸出的函數名無任何修飾且全部大寫。

  b、C++編譯時函數名修飾約定規則:

  __stdcall調用約定:

  1、以“?”標識函數名的開始,后跟函數名;

  2、函數名后面以“@@YG”標識參數表的開始,后跟參數表;

  3、參數表以代號表示:

  X--void ,

  D--char,

  E--unsigned char,

  F--short,

  H--int,

  I--unsigned int,

  J--long,

  K--unsigned long,

  M--float,

  N--double,

  _N--bool,

  ....

  PA--表示指針,后面的代號表明指針類型,如果相同類型的指針連續出現,以“0”代替,一個“0”代 表一次重復;

  4、參數表的第一項為該函數的返回值類型,其后依次為參數的數據類型,指針標識在其所指數據類型前;

  5、參數表后以“@Z”標識整個名字的結束,如果該函數無參數,則以“Z”標識結束。

  其格式為“?functionname@@YG*****@Z”或“?functionname@@YG*XZ”,例如

  int Test1(char *var1,unsigned long)-----“?Test1@@YGHPADK@Z”

  void Test2() -----“?Test2@@YGXXZ”

  __cdecl調用約定:

  規則同上面的_stdcall調用約定,只是參數表的開始標識由上面的“@@YG”變為“@@YA”。

  __fastcall調用約定:

  規則同上面的_stdcall調用約定,只是參數表的開始標識由上面的“@@YG”變為“@@YI”。

  VC++對函數的省缺聲明是"__cedcl",將只能被C/C++調用


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM