C/C++中的函數重載(overloading)


幾年前,我已經介紹過如何使用const和volatile限定詞聲明數據。使用這些限定詞來聲明數據,產生的影響會波及到函數的聲明。在C和C++中,影響是不一樣的,很大程度上是因為C++中的函數聲明比C有更多的功能。為了更清楚描述清楚影響的不同,我們先來看看C和C++中函數最主要的差異是什么。

很多C庫包含至少一組的命名非常接近的函數。每個函數除了參數或返回值不同,功能在本質上是相同的。

標准C庫也包含若干這樣的組。例如,有一組“abs”函數專門用來計算一個數值的絕對值。組里包含:

int abs(int i); long int labs(long int li); float fabsf(float f); double fabs(double d); long double fabsl(long double ld); 

函數fabsf和fabsl不屬於早期C標准,卻存在於修訂后的C9X標准中。

標准C庫還有一個“put”函數組:

int fputc(int c, FILE *f); int fputs(char const *s, FILE *f); int putchar(int c); int puts(char const *s); 

這些函數都用來往文件里寫入,盡管,寫的內容的類型不同,文件的指定方式也有差異。 函數fputc和putchar每次寫一個字節(傳入的其實是int),而puts和fputs每次寫從null結尾字符串中得到的所有字節。函數fputc和fputs寫入的文件是由參數傳入的,而putchar和puts總是往標准輸出中寫。

盡管組中的函數名字不同,但程序員還是把它們當做只有一個名字的函數。例如,從來沒聽過程序員說fputd一個字符或fputs一個字符串,而是說put一個字符或put一個字符串。" 我們通常把函數fputc和fputs當做一個put函數。我們也通常把函數abs。當我們已經明了組里的每一個函數實際上都是不同的函數,那么沒有理由再啰嗦下去了。

重載聲明
讓函數名字跟描述程序行為的名字保持一致是一個良好的編程習慣。每種負責輸出的函數最好是都叫做put。不幸的是,C不允許程序中有同名的函數。

C中獨一無二函數名的限定對函數庫的使用者和作者都是一種負擔。作者需要想象出相近但差異又不能太大的函數名,而使用者需要學會這些不同。一個認真的作者會浪費數小時來設計一組函數名前綴或后綴,以便減少使用者的負擔。

C++通過重載函數名來避免這類麻煩。你可以在同一個程序里使用同名的兩個或多個函數。函數名重載可以讓函數使用起來更“自然”。使用了重載的程序也更容易讀和寫。(當然,過猶不及,過多的重載也並非好事)

C++中聲明重載的函數跟聲明其它函數沒什么不同。只不過它跟其它的某個函數重名。重載函數必須使用不同的變量,否則編譯器沒法區分它們。

例如,可以定義如下的重載函數put:

int put(int c); // putchar int put(int c, FILE *f); // fputc int put(char const *s); // puts int put(char const *s, FILE *f); // fputs

重載決策
當編譯器遇到對函數put的調用,它會選擇函數參數完全匹配的函數聲明進行調用。這個過程叫做重載決策。例如:

  put('a', stdout);

調用如下的函數聲明:

  int put(int c, FILE *f);

而:

  put("Hello\n");

調用:

  int put(char const *s);

如果編譯器沒法找到參數匹配的函數,會報錯。例如,調用:

  put("Error #", n, stderr);

會報錯,因為沒有函數聲明了三個變量。

盡管調用的函數的參數個數必須跟匹配的聲明函數一致,但參數類型卻不必完全一致。C++允許進行一些參數的隱式類型轉換。

例如,函數聲明如下:

int put(int c); int put(int c, FILE *f); 

往一個文件中寫入單個字符。在兩個函數中,參數c的類型是int而不是char,標准C庫使用整型的EOF來表示文件結尾和I/O錯誤。一個char沒辦法表示EOF。一個int可以表示所有的char值和EOF。

盡管函數聲明了int型參數,但你可以使用char參數來調用。例如:

char c; ... put(c); 

重載決策選擇了:

int put(int c); 

作為匹配函數。這里,編譯器把char隱式轉換為int。

最佳匹配和歧義Best matches and ambiguities
有可能會有多個重載函數能夠匹配一個函數調用。例如,下面的四個函數:

void f(int i); void f(long int li); void f(char *p); void f(double d, int i); 

開頭的三個函數可以匹配函數調用f(0)。0可以被看做int,long int或char*類的空指針。第四個函數沒法匹配,因為它需要兩個參數。

當面對多個可選函數時,重載決策根據函數參數類型的隱式轉換來排序,找出最匹配的那個。例如,在調用f(0)時,參數0位int類型,當調用:

void f(char *p); 

需要把int轉換為char*。當調用:

void f(long int li); 

需要把int轉換為long int。而:

void f(int i); 

是非常准確的匹配。准確的匹配永遠是最佳選擇。

那么,假設:

void f(int i); 

沒有被聲明。這種情況下,重載決策必須在下面二者中選一個:

void f(long int li); void f(char *p); 

但它們兩個不分仲伯,都需要進行類型轉換。當重載決策沒有辦法選出一個最優匹配,那么調用就是不明確的,這會產生一個編譯錯誤。

這就是函數名重載的基本知識,更多細節請見下次的文章。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM