昨天筆試的時候碰到一個很有意思的題目,大體如下:
class Parent { public: Parent() { doit(); } ~Parent() { doit(); } virtual void doit() { cout << "I'm Parent!" << endl; } }; class Child: public Parent { public: Child() { } ~Child() { } void doit() { Parent::doit(); cout << "I'm Child!" << endl; } }; int main() { Parent *p_base = new Child(); p_base->doit(); delete p_base; return 0; }
以前在項目中並沒有寫過在構造函數中調用虛函數的情況,這一般也是不允許的,增加了代碼的複雜度(參見Effective C++ 條款9)。這裏謹記C++繼承中構造、析構的順序就不難寫出答案:
I'm Parent! I'm Parent! I'm Child! I'm Parent!
但是我覺得題目應該可以出的更巧妙一些:
class Parent { public: Parent() { doit(); } ~Parent() { doit(); } virtual void doit() { cout << "I'm Parent!" << endl; } }; class Child: public Parent { public: Child() { doit(); } ~Child() { doit(); } void doit() { Parent::doit(); cout << "I'm Child!" << endl; } }; int main() { Parent *p_base = new Child(); p_base->doit(); delete p_base; return 0; }
如果你寫下:
I'm Parent! I'm Parent! I'm Child! I'm Parent! I'm Child! I'm Parent! I'm Child! I'm Parent!
那麼,恭喜你,你答錯了。因爲這個題目根本不會調用子類的析構函數,爲啥?因爲父類的析構函數是非虛函數,但是main函數中在釋放的靜態類型指向父類,所以它根本不調用子類的析構函數。這也是爲什麼在繼承體系中一般都需將父類的析構函數設爲虛函數(參見Effective C++ 條款7),否則有可能造成內存泄露。
正確答案是:
I'm Parent! I'm Parent! I'm Child! I'm Parent! I'm Child! I'm Parent! I'm Child! I'm Parent!