C++函數過載詳解

2020-07-16 10:04:21
C++ 不允許變數重名,但是允許多個函數取相同的名字,只要參數列不同即可,這叫作函數的過載(讀“蟲載”,不讀“眾載”,其英文是 overload)。過載就是裝載多種東西的意思,即同一個事物能完成不同功能。

函數的過載使得 C++ 程式設計師對完成類似功能的不同函數可以統一命名,減少了命名所花的心思。例如,可能會需要一個求兩個整數的最大值的函數,也可能還要寫一個求三個實數的最大值的函數,這兩個函數的功能都是求最大值,那麼就都命名為 Max 即可,不需要一個命名為 MaxOfTwoIntegers,另一個命名為 MaxOfThreeFloats。

在呼叫同名函數時,編譯器怎麼知道到底呼叫的是哪個函數呢?編譯器是根據函數呼叫語句中實參的個數和型別來判斷應該呼叫哪個函數的。因為過載函數的參數列不同,而呼叫函數的語句給出的實參必須和參數列中的形參個數和型別都匹配,因此編譯器才能夠判斷出到底應該呼叫哪個函數。例如下面的程式:
#include <iostream>
using namespace std;
void Max(int a, int b)
{
    cout << "Max 1" << endl;
}
void Max(double a, double b)
{
    cout << "Max 2" << endl;
}
void Max(double a, double b, double c)
{
    cout << "Max 3" << endl;
}
int main()
{
    Max(3, 4);  //呼叫 int Max(int, int)
    Max(2.4, 6.0);  //呼叫 double Max(doubleA double)
    Max(1.2, 3.4, 5);  //呼叫 double Max(double, double, double)
    Max(1, 2, 3);  //呼叫 double Max(double, double, double)
    Max(3, 1.5);  //編譯出錯:二義性
    return 0;
}
以上程式如果去掉第 21 行編譯出錯的語句,輸出結果是:
Max 1
Max 2
Max 3
Max 3

顯然,編譯器根據呼叫 Max 函數的語句所給的實參的個數和型別,可以找到完全匹配的函數。例如第 17 行,實參是兩個整型數,那麼呼叫的當然就應該是原型為 void Max(int, int) 的那個 Max 函數。

第 21 行編譯會出錯,因為兩個實參一個是整型,一個是實數型。如果將整型自動轉換成 實數型,那麼看來應該呼叫 void Max(double, double) 這個函數;可是如果將實數型去尾自動轉換為整型,那麼呼叫 void Max(int, int) 似乎也說得過去。

C++ 設計者認為,此時編譯器應該因不知如何選擇而報告二義性的錯誤,而不是規定優先選擇其中某一種。因為如果硬性規定的話,程式設計師很可能記不清到底編譯器是怎麼規定的,從而可能導致程式設計師心裡認為應該呼叫這個 Max 函數,實際上編譯器的處理是呼叫另一個 Max 函數,結果程式設計師因編譯器不會報錯而無法察覺這個問題。好的工具總是應該讓使用者少犯錯誤,或者犯了錯誤也能馬上發現。

如果去掉 void Max(int, int) 函數或者 void Max(double, double) 函數中的任何一個,則第 21 行就不會導致編譯時的二義性錯誤了,因為此時實參該如何自動轉換才能和 Max 函數匹配是確定的。

在兩個函數同名而引數個數不同,但是其中引數多的那個函數的引數又可以取預設值的情況下,也可能會引發二義性。例如下面兩個函數:
int Sum(int a, int b, int c = 0);
int Sum(int a, int b);
則函數呼叫語句:
Sum (1, 2);
就會在編譯時導致二義性錯誤。因為編譯器不知道是應該以 (1, 2, 0) 作為引數呼叫第一個 Sum 函數,還是以 (1, 2) 作為引數呼叫第二個 Sum 函數。同樣,將編譯器設計成在這種情況下優先選擇某一種也是不合理的。

需要強調一點,同名函數只有參數列不同才能算過載。兩個同名函數的參數列相同而返回值型別不同不是過載,而是重複定義,是不允許的。