請看如下一段代碼:
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(int argc, char* argv[])
{
C c;
return 0;
}
類C從類B中繼承出來,類C中聚合了一個類A的對象,類C的析構函數並未調用類A的析構函數,請問:這段代碼執行後,會調用類A的析構函數釋放內存嗎?經測試,執行結果如下:
Constructor of A
Deconstructor of C
Deconstructor of A
Deconstructor of B
由此可以看出,C的析構函數確實調用了類A的析構函數,該調用是由編譯器隱含加入的,隨後還調用了基類B的析構函數,但是請注意,編譯器只會為基類和聚合類添加對析構函數的調用,如果C中的成員為指向類A的指針,則編譯器不會加入對類A的析構函數的調用。
那麼,如果我們把main函數改變一下:
int main(int argc, char* argv[])
{
B * b = new C;
delete b;
return 0;
}
請問:這段代碼會發生內存洩漏嗎?執行結果如下:
Constructor of A
Deconstructor of B
發生了什麼?調用了A的構成函數分配了內存,卻只調用了B的析構函數,C和A的析構函數都沒有調用,內存沒有釋放,為什麼?
有人也許已經看出了問題所在,B和C的析構函數都應該是虛函數,否則,由於b的類型為指向類B的指針,delete b只會調用B的析構函數。當B和C的析構函數都是虛函數時,編譯器會根據b指向的對象實際上是C的對象,而去調用C的析構函數。請對代碼作如下改變:
class B {
public:
virtual ~B () { printf("Deconstructor of B\n"); }
};
class C : public B {
public:
virtual ~C () { printf("Deconstructor of C\n"); }
private:
A a;
};
再次運行,結果正確:
Constructor of A
Deconstructor of C
Deconstructor of A
Deconstructor of B