凡帶有 virtual 函式的類別,其解構函式應宣告為 virtual。否則透過基底類別指標刪除衍生類別物件時,僅呼叫基底類別解構函式,導致衍生類別資源洩漏、行為未定義。
class TimeKeeper {
public:
TimeKeeper();
~TimeKeeper(); // 非 virtual
};
class AtomicClock : public TimeKeeper { /* ... */ };
class WaterClock : public TimeKeeper { /* ... */ };
TimeKeeper* ptk = getTimeKeeper(); // 回傳衍生類別物件指標
delete ptk; // 僅呼叫 ~TimeKeeper(),衍生類別部分未銷毀 → 資源洩漏
class TimeKeeper {
public:
TimeKeeper();
virtual ~TimeKeeper(); // virtual 解構函式
};
加上 virtual 後,delete ptk 會正確呼叫衍生類別解構函式(如 ~AtomicClock()),再呼叫基底類別解構函式,完整銷毀物件。
std::string、std::vector、STL 容器、boost::unordered_map 等)。警告:繼承標準庫容器或無 virtual 解構函式的類別極易造成上述錯誤,請採用組合而非繼承。
虛擬函式表(vtable)與指標(vptr)會增加每個物件大小(通常一個指標大小)與間接呼叫開銷。若類別無多態需求,避免宣告 virtual 解構函式可節省記憶體與提升效能。
若基底類別為抽象類別,可宣告純虛擬解構函式,但必須提供定義(因衍生類別解構時會呼叫基底版本):
class Base {
public:
virtual ~Base() = 0; // 純虛擬
};
Base::~Base() {} // 必須定義
has-a)。