C++重載函數的定義:同一作用域的多個函數,如果具有相同函數名而同時具有不同的形參清單,它們可以稱為重載函數。
1 重載函數黑體關鍵詞解析
1.1 相同函數名
1)重載函數也是函數,是以需要函數名;函數名隻是為了幫助編譯器判斷調用的是哪個函數而已。
2)相同函數名正是重載函數引入的目的,對于一系列類似的操作省去為函數起名和記住函數名的麻煩,簡化了程式實作,且使程式更容易了解。
1.2 同一作用域
重載函數必須在同一作用域的原因是:C++有局部聲明屏蔽全局聲明的特性。這樣如果在局部作用域有一個與外層作用域的重載函數同名的函數,該函數将屏蔽那些外層的重載函數。
例子如下:
void func(const string &);
void func(const double); //重載func
void f(int ival){
void func(int); //局部聲明
func(”hello“);//error:因為func(const string &)被屏蔽了
func(ival); //ok:局部的func(int)可見
func(1.5);//ok:盡管全局重載函數func(const double)被屏蔽,但是可調用局部func(int),調用時實參1.5隐式轉化為int型
}
但是如果将局部聲明拿到函數外,位于同一作用域後,重載函數ok。
void func(const string &);
void func(const double); //重載func
void func(int); //重載func
void f(int ival)
{
func(”hello“);//ok:調用func(const string &)
func(ival); //ok:調用func(int)
func(1.5);//ok:調用func(const double)
}
1.3 不同形參清單
代碼經過編譯後會生成符号(symbol)表,函數調用是通過符号表中函數符号名來實作的(注意:函數名不同于函數符号名)。 C++中的函數符号名由函數名和形參清單共同決定(函數符号名與函數傳回值沒有任何關系),形參清單包括形參個數和形參類型。
重載函數正是利用C++函數符号名這種特性來實作的,這樣相同函數名如果使用不同形參清單,會生成不同函數符号名(不會出現重複定義的問題)。
判斷不同形參清單時注意事項(注意下面people是類):
(1)形參名僅是幫助文檔,不會影響形參清單
void func(const people &a); //形參名a
void func(people &);
(2)typedef僅提供别名,不會建立新的類型
typedef people p //p是people的别名
void func(const people &);
void func(const p &);
(3)調用帶預設實參函數時,編譯器可以忽略預設實參(這意味着使用者可以給定實參也可以不給定),預設實參不影響參數個數
void func(const people1 &, const people2 &);
void func(const people1 &, const people2 & = ”“); //沒有改變參數個數
(4)const形參
a)對于函數值傳遞形參場合,const與非const是等價的形參;
void func(people1,people2);
void func(const people1, const people2);
b)對于函數引用傳遞形參場合,const與非const是不同的形參;
void func(people &);
void func(const people &); //新函數,是func的重載函數
const people a(1,2);
people b;
func(a);//調用func(const people &)
func(b);//調用func(people &)
c)對于函數指針傳遞形參場合,指向const對象與指向非const對象是不同的形參;
注意:基于指針本身是否為const不能實作函數重載
void func(int *);
void func(int * const); //屬于重複聲明
2 重載函數的函數比對與實參類型轉換
比對的結果有3種可能
1)編譯器找到最佳比對函數,生成調用函數代碼;
2)編譯器找不到比對函數,編譯出錯;
3)找到多個比對函數,但是沒有一個是最佳比對函數,調用具有二義性;
2.1 重載函數比對3個步驟
void func();
void func(int);
void func(int, int);
void func(double, double = 1.2);
func(1.35); //調用func(double, double = 1.2)
1)查找候選函數
與被調用函數同名的函數都可以稱為候選函數。上面例子中候選函數有4個。
2)确定可行函數
可行函數需要滿足2個條件:
a)被調函數實參與函數形參個數相同;
b)被調函數實參與函數參數類型比對,或者可以通過隐式轉化為類型比對 ;
3)決定最佳比對
最佳比對原則:
a)實參與形參類型精确比對優于需要類型轉化後的比對
b)多參數最佳比對特點
i)每個實參比對不劣于其它可行函數;
ii)至少有一個實參優于其它可行函數;
func(1,1.5)調用分析:
第一步候選函數:4個
第二步:可行函數func(int,int)和func(double,double)
第三步:最佳比對,隻看實參數1精确比對func(int,int),再看實參2精确比對的是func(double,double),2個函數形參優劣相當這樣找不出最佳比對,出現了”二義性“。
解決二義性辦法是顯式強制類型轉換,但是強制類型轉換不是最好的,要避免;出現這樣情況表明形參設計不合理。
func(static_cast<double>(1), 1.5)//調用func(double,double)
func(1, static_cast<int>(1.5)) //調用func(int,int)
2.2 實參類型轉換
實參類型轉換分為4級:
1)精确比對,實參與形參類型一緻
2)通過類型提升實作比對;
3)通過标準轉換實作比對;
4)通過類類型轉換實作比對;
注意:
a)類型提升實作比對優于标準轉換實作的比對
extern void func(long);
extern void func(float);
func(3.14); //造成二義性,因為都是使用标準轉換,沒有說哪個更優
b)較小整型提升為int型
extern void func(int);
extern void func(short);
func('c'); //char提升為int,比對到函數func(int)
c)枚舉對象隻能用同類型枚舉對象或者枚舉成員初始化
enum Test {T1 = 1, T2 = 130};
extern void func(Test);
extern void func(int);
void main()
{
Test test = T1;
func(10);//調用func(int)
func(T1); //調用func(Test)
func(test);//調用func(Test)
return 0;
}
d)枚舉值可以傳遞給整型形參,整型值不能傳遞給枚舉形參
enum Test {T1 = 1, T2 = 130};
extern void func(int);
<pre name="code" class="cpp">extern void func(unsigned char);
unsigned char uchar = 130;
func(T2); //調用
func(uchar);//調用
3 參考資料
C++ primer