當使用基類指標刪除衍生類物件時,若基類解構函數非虛擬,編譯器僅呼叫基類解構函數,導致衍生類資源未釋放,造成記憶體洩漏。這是 C++ 物件模型中最核心、也最容易被忽視的規則之一。
class ClxBase {
public:
ClxBase() {}
virtual ~ClxBase() {} // 虛擬解構函數
virtual void DoSomething() {
cout << "Do something in class ClxBase!" << endl;
}
};
class ClxDerived : public ClxBase {
public:
ClxDerived() {}
~ClxDerived() {
cout << "Output from the destructor of class ClxDerived!" << endl;
}
void DoSomething() override {
cout << "Do something in class ClxDerived!" << endl;
}
};
// 測試碼
ClxBase* pTest = new ClxDerived;
pTest->DoSomething();
delete pTest; // 正確呼叫 ClxDerived::~ClxDerived() 再呼叫 ClxBase::~ClxBase()
輸出結果:
Do something in class ClxDerived!
Output from the destructor of class ClxDerived!
class ClxBase {
public:
ClxBase() {}
~ClxBase() {} // 非虛擬解構函數
virtual void DoSomething() { ... }
};
// 其餘相同
輸出結果:
Do something in class ClxDerived!
衍生類解構函數完全未被呼叫,若其中釋放記憶體、關閉檔案、釋放鎖等資源,將直接導致資源洩漏。
delete pBase 透過 vptr 找到正確的解構函數入口,實現動態綁定| 場景 | 建議 | 理由 |
|---|---|---|
| 類別設計為基類,可能被繼承 | 必須 virtual | 確保多態刪除正確性 |
| 類別為 final(C++11),或明確不作為基類 | 可非 virtual | 避免 vtable 額外儲存開銷 |
| 純介面類別(只有 pure virtual 函數) | 必須 virtual | 介面本身即為多態契約 |
std::unique_ptr, std::shared_ptr)自動管理生命週期,減少手動 deletevirtual ~Base() = default;final 關鍵字明確禁止繼承,編譯器可優化移除 vtable虛擬解構函數不是選項,而是多態類別設計的強制規範。養成習慣,從架構源頭杜絕記憶體洩漏隱患。