在 C++ 多型設計中,當我們透過基類指標刪除派生類物件時,若基類析構函數未宣告為 virtual,將導致資源洩漏。編譯器僅會呼叫基類的析構函數,派生類的析構函數不會被執行,造成派生類持有的資源(記憶體、檔案控制代碼、Socket 等)無法正確釋放。
只要一個類被設計為多型用途的基類(即包含至少一個 virtual 函數),它就必須擁有虛析構函數。這是 C++ 核心準則(C.121)的強制要求。反之,若類不打算作為基類,或不涉及多型行為,則不應將析構函數宣告為 virtual,否則會引入虛表指標(vptr),增加每個物件的記憶體開銷(通常 8 位元組於 64 位元系統)。
class Base { public: Base(); ~Base(); }; // 非虛析構函數
class Derived : public Base { /* ... */ };
Base* p = new Derived;
delete p; // 錯誤:僅呼叫 ~Base(),~Derived() 被略過 → 資源洩漏
class Base {
public:
Base();
virtual ~Base(); // 加上 virtual
};
若某類別沒有任何成員函數適合宣告為純虛函數,但又想強制其成為抽象類(不可實例化),可將析構函數宣告為純虛:
class Abstract {
public:
virtual ~Abstract() = 0; // 純虛析構函數
};
// 必須提供定義(即使是空的),否則連結失敗
Abstract::~Abstract() {}
關鍵點:純虛析構函數必須提供定義。因為派生類析構時會隱式呼叫基類析構函數,若無定義會導致連結錯誤。這是 C++ 唯一「純虛函數仍需定義」的例外。
C++ 保證析構順序:最深層派生類 → 依序向上呼叫每層基類析構函數。虛析構函數機制確保此順序在透過基類指標刪除時依然生效。
virtual 函數 → 必須宣告 virtual ~ClassName()virtual(避免 vptr 開銷)virtual ~Class() = 0; 並提供定義