解構函式的呼叫順序與虛擬機制是 C++ 記憶體管理的核心。本文透過實測程式碼,揭示編譯器隱含插入的解構函式呼叫順序,並說明為何基類別解構函式必須宣告為 virtual 以避免記憶體洩漏。
class A {
public:
A() { pValue = new int[100]; printf("Constructor of A\n"); }
~A() { delete[] pValue; printf("Deconstructor of A\n"); }
private: int* pValue;
};
class B { public: ~B() { printf("Deconstructor of B\n"); } };
class C : public B {
public: ~C() { printf("Deconstructor of C\n"); }
private: A a;
};
int main() { C c; return 0; }
輸出順序:
Constructor of A Deconstructor of C Deconstructor of A Deconstructor of B
編譯器隱含在 C::~C() 中呼叫 a.~A(),再呼叫基類別 B::~B()。若成員是指標而非物件,編譯器不會自動呼叫 delete。
int main() { B* b = new C; delete b; }
輸出僅:
Constructor of A Deconstructor of B
C::~C() 與 A::~A() 均未被呼叫,導致 new int[100] 記憶體洩漏。原因:delete b 靜態綁定至 B::~B()。
class B { public: virtual ~B() { ... } };
class C : public B { public: virtual ~C() { ... } };
再次執行,輸出正確:
Constructor of A Deconstructor of C Deconstructor of A Deconstructor of B
虛擬解構函式確保 delete 透過虛函數表動態綁定至實際物件類型的解構函式,完整釋放資源。
delete,編譯器不介入。virtual。