class Base {
public:
virtual void foo() { std::cout << "Base::foo\n"; }
~Base() { std::cout << "~Base\n"; } // 非 virtual
};
class Derived : public Base {
public:
void foo() override { std::cout << "Derived::foo\n"; }
~Derived() { std::cout << "~Derived\n"; }
};
int main() {
Base* p = new Derived;
p->foo(); // 輸出 Derived::foo (虛函式正常多態)
delete p; // 僅輸出 ~Base!~Derived 未被呼叫 → 記憶體洩漏
}
delete p 中 p 靜態型別為 Base*。Base::~Base()(靜態綁定),完全忽略動態型別 Derived。virtual 後,解構函數進入虛函式表,delete 時透過 vptr 動態分派到 Derived::~Derived(),再自動調用基類解構函數,形成完整銷毀鏈。含虛函式的類別,編譯器會在物件開頭植入 vptr(指向虛函式表)。每個類別有一張 vtable,儲存虛函式位址。virtual ~Base() 確保 vtable 中有解構函數入口,delete 透過 vptr → vtable → ~Derived() 實現多態銷毀。
| 場景 | 建議 | 理由 |
|---|---|---|
作為基類、可能被 delete base_ptr | 必須 virtual | 保證派生類資源正確釋放 |
| 純介面類別(只有純虛函式) | 必須 virtual | 同左,且常定義為 virtual ~Interface() = default; |
最終類別(final)、不作為基類 | **不需要** virtual | 無繼承鏈,虛函式表額外開銷(vptr + vtable)可避免 |
標準庫容器元素型別(如 std::vector<Value>) | **不需要** virtual | 值語義、無多態刪除,避免物件體積膨脹 |
class Base { public: virtual ~Base() = default; };virtual,寫 ~Derived() = default; 即可。std::unique_ptr<Base> p = std::make_unique<Derived>(); —— 甚至不需要手寫 delete,RAII 自動正確銷毀。誤區:「所有類別的解構函數都應該是 virtual。」
事實:只有「作為多態基類」才需要。virtual引入 vptr(通常 8 位元組)與 vtable,對大量小物件(如std::vector<Point>)會造成顯著記憶體與效能開銷。
virtual 解構函數是 C++ 多態銷毀的基石。記住鐵律:若類別可能被繼承且透過基類指標刪除,解構函數必須是 virtual。現代 C++ 配合智慧指標與 = default,讓資源管理既安全又零成本。