C++ 中,基類指針/引用指向派生類對象時,調用虛函數會根據對象的實際類型(而非指針類型)動態綁定到派生類的覆蓋版本。這是面向對象多態的基石。
class Base {
public:
void m() { cout << "Base::m()\n"; } // 非虛函數
virtual void f() { cout << "Base::f()\n"; } // 虛函數
};
class Sub : public Base {
public:
void m() { cout << "Sub::m()\n"; } // 隱藏 Base::m()
void f() { cout << "Sub::f()\n"; } // 覆蓋 Base::f()
};
int main() {
Base* base = new Sub;
base->f(); // 輸出 "Sub::f()" —— 動態綁定
base->m(); // 輸出 "Base::m()" —— 靜態綁定
delete base;
}
| 特性 | 虛函數 f() | 普通函數 m() |
|---|---|---|
| 綁定時機 | 運行時(動態) | 編譯時(靜態) |
| 指針調用行為 | 調用實際對象類型的版本 | 調用指針聲明類型的版本 |
| 實現機制 | 虛函數表 (vtable) + 虛指針 (vptr) | 直接函數調用 |
基類指針刪除派生類對象時,若基類析構函數非虛,僅調用基類析構函數,派生類析構函數不被調用 → 派生類資源(如動態分配內存)未釋放 → 內存洩漏。
class Base { ~Base() { cout << "Base destroyed\n"; } };
class Sub : public Base { ~Sub() { cout << "Sub destroyed\n"; } };
// ...
Base* p = new Sub;
delete p; // 僅輸出 "Base destroyed" —— Sub 析構函數未調用!
class Base {
public:
virtual ~Base() { cout << "Base destroyed\n"; } // 關鍵:virtual
};
class Sub : public Base {
public:
~Sub() { cout << "Sub destroyed\n"; }
};
// ...
Base* p = new Sub;
delete p; // 輸出 "Sub destroyed" 然後 "Base destroyed" —— 正確釋放
virtual void f() = 0; 使類成為抽象類,強制派生類實現。virtual。override (C++11),防止簽名錯誤。虛函數是 C++ 多態的核心機制,虛析構函數是資源管理的安全網。深入理解其背後的 vtable/vptr 模型、調用順序及性能影響,能寫出更健壯、高效的 C++ 代碼。