C++函數重載

什麼是函數重載

學過C語言的同學應該很清楚,在C語言中,同一個程序中是不能定義多個名稱相同的函數,否則編譯會報重定義的錯誤信息,但是C++中則允許定義多個名稱相同的函數,在C++中,這稱之爲函數重載,讓我們來看看更官方一點的定義,函數重載是指在同一作用域內,可以有一組具有相同函數名,不同參數列表的函數,這組函數被稱爲重載函數。 此外需要注意的是,函數的返回值不構成重載條件。看下面幾組示例。

//類A和類B的兩個同名show()函數不構成重載//因爲兩個函數的作用域不一樣class A{ public:

void show(int x){} };class B{

public:

void show(double x){} };

//類A兩個同名show()函數不構成重載//因爲兩個函數的參數一樣,返回值類型不同不能構成重載class A{

public:

void show(int x){}

int show(int x){}};

//類A兩個同名show()函數構成重載//因爲兩個同名函數作用域相同,且參數列表不一樣class A{

public:

void show(int x){}

void show(double x){}};

注:重載函數的條件之一參數列表不同包括參數個數不同或者參數類型不同或者參數順序不同都可以。

函數重載的好處

先想想下面一個場景,如果一個程序要實現一組加法操作,既要能夠處理兩個整數,又要處理兩個字符串相加,你會如何做了? 如果是C語言,你必須爲這組函數取不同的名字,如add_int, add_str等等, 是不是程序的可讀性不太好。如果是C++實現,由於其支持函數重載,因此可以用一個函數名add就OK了,這樣就避免了名字空間的污染,提高了程序的可讀性。

再想想,如果沒有函數重載機制,每個類只能存在一個構造函數(因爲構造函數名字必須與類名相同),因此,要想以不同的方式實例化類對象,就會變的相當麻煩。

編譯器如何解決命名衝突

我們定義兩個重載函數如下圖所示,然後對生成的可執行利用objdump -d a.out命令進行反彙編觀察,可以看出,int add(int x, int y)編譯之後其函數簽名變爲__Z3addii,函數float add(float x, float y)編譯之後其函數簽名變爲__Z3addff, 不難發現,經過編譯之後,函數名變的不那麼單純了,會增加一些其它的信息進去,具體說來,編譯之後的函數名會包含返回值類型的信息、參數列表信息等等。這種技術叫命名修飾。
在這裏插入圖片描述
在這裏插入圖片描述
不同編譯器的命名修飾規則也不一樣,這裏就不深究了,我們只要知道C++中是通過這種機制來解決函數重載命名衝突的就好了。

extern “c” {}作用

通過前面分析可知,C++是一個面嚮對象語言,它支持函數重載,而C語言中並沒有函數重載,編譯器在編譯C++程序和C語言時的機制有些不同,比如說對於同一個函數int add(int x, int y);其函數名在C++中將被編譯爲__Z3addii ;而在C語言中可能就是直接編譯爲__add。

因此,如果C++中含有C語言代碼時,就可能會出問題。 因爲在編譯時C++編譯器對C代碼的函數也會進行名字修飾,函數名變了以後,將導致在C運行庫中找不到對應函數,發生鏈接錯誤。比如說對於以下代碼:

//print.cppint printf(const char *format,…);int main(){

printf(“Hello world!”);

return 0;}

看看編譯時發生了什麼:

bogon:0807 lizhong$ g++ -o print print.cppUndefined symbols for architecture x86_64:

“printf(char const*, …)”, referenced from:

_main in print-f04c36.old: symbol(s) not found for architecture x86_64clang: error: linker command failed with exit code 1 bogon:0807 lizhong$ cat print.cpp

是不是,鏈接過程出現了錯誤,原因就是前面所說的,想想也是,printf函數是C標準庫定義的函數,其編譯時按照C語言編譯規則,函數名printf編譯爲_printf(這裏只是假設,就是這麼個意思);而在print.cpp中,對printf的調用時按照C++編譯規則編譯,編譯成了_printf_XXX,鏈接的時候又怎麼能找得到呢?

因此爲了防止C++編譯器對調用的C代碼在編譯時進行名字修飾,我們將C代碼用extern “C”進行鏈接指定,告訴編譯器,不要對這部分代碼進行名字修飾,而是生成符合C規則的中間符號名。如下所示:

//print.cppextern “C”{ int printf(const char *format,…);}int main(){

printf(“Hello world!”);

return 0;}

好了,現在代碼就能夠正常運行了,我想大家也應該清楚extern "C"的作用了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章