| # | 規則 | 關鍵點 |
|---|---|---|
| 1 | 聲明處加 virtual | 定義處可加可不加,建議一致性 |
| 2 | 基類虛函數自動傳遞 | 派生類同名同簽名函數自動為虛函數 |
| 3 | 派生類未覆寫則用基類版本 | 繼承而非覆寫時保持基類行為 |
| 4 | 多態需同名同簽名覆寫 | 簽名不同(如加參數)不構成多態,調用基類版本 |
| 5 | 構造函數不能為虛函數 | 構造機制非繼承,虛函數無意義 |
| 6 | 析構函數應為虛函數 | 基類指針刪除派生物件時正確調用派生析構 |
#include <iostream>
using namespace std;
class Base {
public:
virtual void func();
virtual void func(int);
};
void Base::func() { cout << "Base::func()" << endl; }
void Base::func(int n) { cout << "Base::func(int)" << endl; }
class Derived : public Base {
public:
void func(); // 覆寫:同名同簽名 → 多態
void func(char*); // 新函數:不同簽名 → 隱藏非多態
};
void Derived::func() { cout << "Derived::func()" << endl; }
void Derived::func(char* s) { cout << "Derived::func(char*)" << endl; }
int main() {
Base* p = new Derived();
p->func(); // 輸出 Derived::func() → 多態!
p->func(10); // 輸出 Base::func(int) → 派生類未覆寫
// p->func("str"); // 編譯錯誤!基類指針無法訪問派生類新增成員
}
p->func():基類有 virtual void func(),派生類覆寫 → 動態綁定到 Derived::func()p->func(10):派生類無 func(int) → 靜態綁定基類版本p->func("str"):基類無此簽名 → 編譯錯誤(靜態類型檢查)物件記憶體模型不含函數資訊,編譯器如何找到正確函數?答案在於 虛函數表 (vtable) 與 虛指針 (vptr):
vptr 指向其類的 vtablevptr → vtable[槽位] → 函數地址 → 間接調用詳細機制見《虛函數表與動態綁定》章節。
| 場景 | 建議 | 理由 |
|---|---|---|
| 類作為基類,功能預期被派生類修改 | 聲明 virtual | 支援多態擴展 |
| 類作為基類,功能固定不變/派生類用不到 | 不聲明 virtual | 避免 vtable 開銷,語義明確 |
| 析構函數 | 幾乎總是 virtual | 防止資源洩漏,RAII 基石 |
| 構造函數 | 絕不 virtual | 機制不支援,無意義 |
| 私有虛函數 | 可行(NVI 模式) | 基類控制流程,派生類實現細節 |