HOWTO · C++

C++ 中的函式過載 VS 覆蓋

本文演示如何在 C++ 中使用函式過載 VS 覆蓋

本頁內容

函式多載與函式覆寫在 C++ 中解決的是不同問題。函式多載是在同一個作用域中,讓多個函式使用相同名稱但採用不同的參數列表;編譯器會根據呼叫時提供的引數選出符合的函式。函式覆寫則是讓衍生類別以相符的實作取代繼承而來的 virtual 函式;透過基底類別參考或指標呼叫時,執行階段會將呼叫分派給衍生類別物件。

在 C++ 中,函式多載與函式覆寫的意義

函式多載是從參數不同的函式中選出要呼叫的版本;函式覆寫則是在繼承階層中替換行為。單靠函式的回傳型別無法區分多載,而函式覆寫要求基底類別中的函式是 virtual。在衍生類別的宣告中加上 override,可讓編譯器檢查它是否確實符合基底類別的虛擬函式。

函式多載的運作方式

在同一個作用域中宣告多個同名函式,並為它們指定不同的參數列表。參數列表可以有不同數量的參數,或採用不同型別。呼叫函式時,多載解析會根據引數及候選函式的參數型別來選擇版本。回傳型別不會用來選擇多載,因此兩個宣告若只有回傳型別不同,就不能構成有效的多載集合。

執行這個範例:

本節中的程式碼以及顯示的標籤或輸出仍為英文。

#include <iostream>

class Formatter {
public:
    void show(int value) const {
        std::cout << "integer: " << value << '\n';
    }

    void show(double value) const {
        std::cout << "decimal: " << value << '\n';
    }
};

int main() {
    Formatter formatter;
    formatter.show(7);
    formatter.show(2.5);
}
integer: 7
decimal: 2.5

提供的執行證據確認這個範例已成功執行,且輸出如所示。整數與小數引數會選到不同的 show 多載。新增多載時,請確認參數列表有實質差異,並確保呼叫不會產生歧義。

函式覆寫的運作方式

基底類別將函式宣告為 virtual;衍生類別則提供名稱、參數列表、相容限定詞與回傳型別相符的函式。請在衍生類別的宣告中標記 override,如此一來,若有不相符之處,編譯器就會在編譯時提出診斷。透過基底類別參考或指標呼叫函式時,虛擬派發會依物件選出最終覆寫函式。

執行這個範例:

本節中的程式碼以及顯示的標籤或輸出仍為英文。

#include <iostream>

class Message {
public:
    virtual void print() const {
        std::cout << "message\n";
    }
};

class Alert : public Message {
public:
    void print() const override {
        std::cout << "alert\n";
    }
};

int main() {
    Alert alert;
    const Message& message = alert;
    message.print();
}
alert

提供的執行證據確認這個範例已成功執行,且輸出如所示。message 的型別雖然是基底類別,但它參照的是 Alert,因此虛擬呼叫會使用 Alert::print。基底類別也可以將函式宣告為純虛擬函式,寫成 = 0;這樣的類別是抽象類別,必須由具體衍生類別實作其純虛擬函式後,才能建立物件。

函式多載與函式覆寫:主要差異

面向 函式多載 函式覆寫
目的 為不同引數提供同名操作 針對衍生型別特化繼承而來的行為
條件 同一作用域中有不同的參數列表 存在繼承關係,且基底類別有相符的 virtual 函式
選擇方式 編譯時根據呼叫引數進行多載解析 執行時透過基底類別參考或指標進行虛擬派發
實用檢查 確認呼叫會選到預期的多載 加上 override,確認宣告確實相符

多載不需要繼承或虛擬函式也能成立。覆寫則仰賴繼承與虛擬派發;它不會因為傳入不同的引數列表而被選中。

常見錯誤與辨識方式

衍生類別中有同名函式,不代表它一定覆寫了基底類別的函式。如果基底函式不是 virtual,衍生類別的宣告會隱藏基底類別中的同名函式,而不會啟用虛擬派發。如果參數、const 限定詞或函式型別的其他部分不相符,預期的覆寫就可能失敗;加上 override 可以在編譯時揭露這類不相符之處。

不要只靠更改回傳型別來建立多載。另外,若需要多型行為,避免以值傳遞衍生物件:將衍生物件複製到基底類別物件時,會發生物件切片,衍生部分會被移除。請使用基底類別參考或指標,以保留動態派發。一般情況下,優先使用自動物件或智慧指標管理所有權;若透過基底類別指標刪除衍生物件,基底類別應有虛擬解構函式。