天天看點

C++重載函數分析

    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

繼續閱讀