在C原因中,一個函數不能與另一個函數重名,而在C++中,只要一個函數的參數列表與另一個函數的參數列表不完全相同,函數名就可以相同。而C++這一特點就是函數的重載。
同一個名字因為參數列表不同,展現了不同的結果,也叫靜多態。
①函數名相同,函數參數列表不同(類型、個數、順序) ②匹配原則1:嚴格匹配,找到再調用 ③匹配原則2:通過隱式類型轉換尋求一個匹配,找到則調用 ④返回值類型不構成重載條件
/*要是放在C語言中,這段代碼必然是編譯不通過,而在C++中是合乎重載原則的*/#include <iostream>using namespace std;float abs(float i){ return (i >= 0 ? i : -i);}double abs(double i){ return (i >= 0 ? i : -i);}int abs(int i){ return (i >= 0 ? i : -i);}int main(){ /*abbiguous二義性*/ float i = abs(-5.5f);/*默認調用第一個abs*/ float j = abs(-4.4);/*默認調用第二個,如果注銷掉第二個abs,編譯時出錯報二義性錯誤*/ cout << i << endl; cout << j << endl; /*浮點數默認大小(類型)*/ cout << "sizeof(4.5)=" << sizeof(4.5) << endl;/*default*/ cout << "sizeof(4.5f)=" << sizeof(4.5f) << endl; return 0;}注意: 重載時的二義性:如果計算機存在有兩種隱式轉換選擇,計算機不會去選,而報二義性錯誤 eg1:double可以隱式轉換為float或int,如果abs(-4.4)并且定義float與int分別作為參數的ads(),編譯時則會產生二義性 eg2:double->int/float會產生兩義性,int->long/double也會產生兩義性。 即兩個特例重載時需要注意。 為了避免重載中的二義性問題,使用時按所需強制轉換,不要讓計算機去自己選擇
對于下面這段程序來說:
#include <iostream>using namespace std;//不設置,C++編譯器默認傾軋void func(int a){cout<<"a = "<<a<<endl;}void func(char a){cout<<"a = "<<a<<endl;}void func(int a, char b){cout<<"a = "<<a<<endl<<"b = "<<b<<endl;}void func(char a, int b){cout<<"a = "<<a<<endl<<"b = "<<b<<endl;}int main(void){ int a = 10; char b = 'b'; func(a); func(b); func(a,b); func(b,a); return 0;}上面的程序在經過C++編譯器編譯時就類似于變成了下面這種寫法,這種寫法與其重載函數名以及參數類型有關:
#include <iostream>using namespace std;/*定義自動傾軋*/void func_i(int a){cout<<"a = "<<a<<endl;}void func_c(char a){cout<<"a = "<<a<<endl;}void func_ic(int a, char b){cout<<"a = "<<a<<endl<<"b = "<<b<<endl;}void func_ci(char a, int b){cout<<"a = "<<a<<endl<<"b = "<<b<<endl;}int main(void){ int a = 10; char b = 'b'; /*調用也自動傾軋*/ func_i(a); func_c(b); func_ic(a,b); func_ci(b,a); return 0;}在C++中,定義階段與聲明操作階段均會進行傾軋(編譯時傾軋),使用extern “C”,可以將某函數設置為不傾軋,可C++需要傾軋以支持重載,為什么弄一個不傾軋出來呢?
分析: 首先,函數聲明時與定義時要么都傾軋,要么都不傾軋,必須得一一對應,否則會報函數找不到的錯誤。傾軋是編譯時進行的,而對于C++要兼容C的問題,C++就必須兼容C的語法與C庫(鏈接庫),C庫只在連鏈接時加入,不存在讓C++編譯器去傾軋的問題;而C頭文件中聲明的C庫函數在調用時會傾軋,要想使用不參加傾軋的C庫函數,C++中編譯時就不能傾軋C的頭文件中對于庫函數的聲明,即C庫中已經不能修改不傾軋為傾軋,則必須將頭文件中的聲明也設置為不傾軋,以此與庫中相互對應。如果查看C的標準頭文件,可以發現其中有一個extern “C”,表示不傾軋C的函數聲明。如下是string.h頭文件中的一部分:
/*查看string.h,發現在函數聲明之前,就對C++編譯器的編譯方式進行聲明extern "C",即聲明為:C++編譯器在編譯時不傾軋*/#ifndef _INC_STRING#define _INC_STRING#ifdef __cplusplus //如果是C++編譯器就要進行不傾軋設置extern "C" {#endif... //函數聲明等#ifdef __cplusplus//與上面匹配}#endif...分別查看不同集成承環境中的string.h文件,都是有對函數的extern “C”不傾軋限定:
舉例說明:
#include <iostream>#include <string.h>using namespace std;extern "C"{ void func(int a){cout<<"a = "<<a<<endl;}}int main(void){ int a = 10; func(a); return 0;}由于定義時不傾軋,而調用時傾軋,經過C++編譯器,其代碼成為:
#include <iostream>using namespace std;/*定義時設置成不傾軋,在編譯時,其函數名依舊相同*/void func(int a){cout<<"a = "<<a<<endl;}int main(void){ int a = 10; /*經C++編譯器,函數名變為傾軋后的函數名*/ func_i(a); return 0;}這樣的代碼是不能夠完成編譯的。而要上面的函數能夠正常調用,要么定義與調用時均遵循默認的傾軋,要么均設置為不傾軋,做法如下:
/*main.cpp*/#include<iostream>#include"func.h"using namespace std;int main(void){ int a = 10; func(a); return 0;}/*func.c*/#include<iostream>using namespace std;extern "C"{ void func(int a){cout<<"a = "<<a<<endl;}}/*func.h*/#ifndef FUNC_H_#define FUNC_H_extern "C" void func(int);//聲明為不傾軋,調用根據聲明調用,自然不傾軋#endif將函數聲明與函數定義時,都設置為不傾軋即可。當然均不設置,采用默認也是可以編譯通過的。但是聲明與定義兩部分只要任意一個設置為傾軋,另一個設置為不傾軋,編譯就不能通過。
測試結果如下:
聲明中不加extern “C”,編譯出錯: 聲明中加extern “C”,編譯不出錯:
總之一句話,傾軋是C++為了實現函數重載而設計的,不傾軋的extern “C”則是為了兼容C而后實現的。我們編程一般犯不著對自定義的C++函數設置。
新聞熱點
疑難解答
圖片精選