C++——重載原理分析

C不支持函數重載,C++支持程序員

代碼演示ide

main.c函數

 1 #include<stdio.h>
 2 
 3 void Max(int a, int b)
 4 {
 5     printf("%d  ", a > b ? a : b) ;
 6 }
 7 
 8 void Max(double a, double b)
 9 {
10     printf("%f  ", a > b ? a : b);
11 }
12 
13 int main()
14 {
15     Max(1,2);
16     Max(1.0, 2.0);
17     return 0;
18 }
View Code

因爲C不支持重載,上述代碼編譯時報錯 錯誤 C2084 函數「void Max(int,int)」已有主體spa

main.cpp指針

 1 #include<stdio.h>
 2 
 3 void Max(int a, int b)
 4 {
 5     printf("%d  ", a > b ? a : b) ;
 6 }
 7 
 8 void Max(double a, double b)
 9 {
10     printf("%f  ", a > b ? a : b);
11 }
12 
13 int main()
14 {
15     Max(1,2);
16     Max(1.0, 2.0);
17     return 0;
18 }
View Code

正常編譯執行。code

代碼是同樣的,僅僅是改個文件後綴,至關於從C編譯器換成C++編譯器,差別竟如此之大。代碼裏面咱們定義2個Max函數,Max這個函數名字是給咱們程序員看的,並非編譯器看的。編譯器看代函數的視角決定了是否支持重,編譯器也是以這種視角來區分不一樣函數的。下面介紹編譯器識別函數名字規則。

blog

C/C++名字修飾約定字符串

如下截圖在VC6.0下生成原型

C編譯器名字修飾約定編譯器

C的名字修飾約定比較簡單粗暴,他不考慮返回值以及參數,只看函數名。而後,無論是啥函數名,在編譯器眼中統一是_函數名。因此main.c那個代碼編譯報錯緣由就很顯而易見了,編譯器發現兩個_Max函數,因而報錯 XXX函數已有主體。

C++編譯器名字修飾約定

C++名字約定不只要考慮函數名字,還要考慮參數,返回值。具體見名字約定

名字約定

修飾名(Decoration name) 

「C」或者「C++」函數在內部(編譯和連接)經過修飾名識別。

修飾名是編譯器在編譯函數定義或者原型時生成的字符串。

有些狀況下使用函數的修飾名是必要的,如在模塊定義文件裏頭指定輸出「C++」重載函數、構造函數、析構函數,又如在彙編代碼裏調用「C」」或「C++」函數等。

修飾名由函數名、類名、調用約定、返回類型、參數等共同決定。

名字修飾約定隨調用約定和編譯種類(C或C++)的不一樣而變化

修飾名隨編譯種類和調用約定的不一樣而不一樣,下面分別說明。

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

__stdcall

約定在輸出函數名前加上一個下劃線前綴,後面加上一個「@」符號和其參數的字節數,格式爲_functionname@number。

__cdecl

約定僅在輸出函數名前加上一個下劃線前綴,格式爲_functionname。

__fastcall

約定在輸出函數名前加上一個「@」符號,後面也是一個「@」符號和其參數的字節數,格式爲@functionname@number。

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

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

__stdcall

①以「?」標識函數名的開始,後跟函數名

②函數名後面以「@@YG」標識參數表的開始,後跟參數表

③參數表以代號表示

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」表明一次重複;

參數表的第一項爲該函數的返回值類型

其後依次爲參數的數據類型,指針標識在其所指數據類型前

參數表後以「@Z」標識整個名字的結束

若是該函數無參數,則以「Z」標識結束。

其格式爲「?functionname@@YA*****@Z」或「?functionname@@YG*XZ」,例如

 __cdecl

規則同上面的_stdcall調用約定,只是參數表的開始標識爲「@@YA」。

__fastcall

規則同上面的_stdcall調用約定,只是參數表的開始標識爲「@@YI」。

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

CB在輸出函數聲明時使用4種修飾符號

//__cdecl

cb的默認值,它會在輸出函數名前加_,並保留此函數名不變,參數按照從右到左的順序依次傳遞給棧,也能夠寫成_cdecl和cdecl形式。

//__fastcall

她修飾的函數的參數將盡肯呢感地使用寄存器來處理,其函數名前加@,參數按照從左到右的順序壓棧;

//__pascal

它說明的函數名使用Pascal格式的命名約定。這時函數名所有大寫。參數按照從左到右的順序壓棧;

//__stdcall

使用標準約定的函數名。函數名不會改變。使用__stdcall修飾時。參數按照由右到左的順序壓棧,也能夠是_stdcall;

extern 「C」

在C++代碼中常常看到在函數以前加上extern "C",代碼以下

 1 #include<stdio.h>
 2 
 3 extern "C" void Max(int a, int b)
 4 {
 5     printf("%d  ", a > b ? a : b);
 6 }
 7 
 8 void Max(double a, double b)
 9 {
10     printf("%f  ", a > b ? a : b);
11 }
12 
13 int main()
14 {
15     Max(1, 2);
16     Max(1.0, 2.0);
17     return 0;
18 }
View Code

這種作法的本質是讓C++編譯器不採用C++方式編譯函數,而是採用C的方式編譯。這樣致使編譯器看待函數名稱是不一樣的

上面代碼是能夠編譯過的,若是給另外一個Max函數也使用extern "C"則沒法編譯經過,道理也很顯然,此時代碼按照C變異角度看有2個_Max函數,所以編譯不過。

 1 #include<stdio.h>
 2 
 3 extern "C" void Max(int a, int b)
 4 {
 5     printf("%d  ", a > b ? a : b);
 6 }
 7 
 8 extern "C" void Max(double a, double b)
 9 {
10     printf("%f  ", a > b ? a : b);
11 }
12 
13 int main()
14 {
15     Max(1, 2);
16     Max(1.0, 2.0);
17     return 0;
18 }
View Code

重載爲何不能只靠返回值的不一樣而進行重載

函數只有返回值不一樣不能夠構成重載,代碼以下,假設以__cdecl方式調用

#include<stdio.h>

//?Max@@YAHHH@Z
void Max(int a, int b)
{
    printf("%d  ", a > b ? a : b);
}

//?Max@@YANHH@Z
void Max(int a, int b)
{
    printf("%f  ", a > b ? a : b);
}

int main()
{
    Max(1, 2);
    Max(1.0, 2.0);
    return 0;
}
View Code

咱們在調用函數時並無寫函數返回值,形如(沒有這麼寫的,這麼寫是錯的)

int Max(1, 2);
double Max(1.0, 2.0);

這樣就能夠依據返回值區分不一樣函數調用。但現實狀況是函數調用依據函數名和參數,調用時是不清楚返回值的。若是函數名,參數都同樣,編譯器就懵逼了

相關文章
相關標籤/搜索