構造函數不可以是虛函數的,這個很顯然,畢竟虛函數都對應一個虛函數表,虛函數表是存在對象內存空間的,如果構造函數是虛的,就需要一個虛函數表來調用,但是類還沒實例化沒有內存空間就沒有虛函數表,這根本就是個死循環。
可是析構函數卻要定義成虛函數,這是為什么呢,寫一個非常簡單的例子來理解一下:
class AA { public: AA() {}; ~AA() { fun2(); }; virtual void fun1() { cout << "Base construct" << endl; } virtual void fun2() { cout << "Base destruct" << endl; } }; class BB : public AA { public: BB() { fun1(); }; ~BB() { fun2(); }; void fun1() { cout << "Derive construct" << endl; } void fun2() { cout << "Derive destruct" << endl; } }; int main() { for (int i = 0; i < 1; i++) { //AA a; BB b; } return 0; }
輸出結果:

所以可以看出,派生類對象構造的時候先調用基類的構造函數再調用派生類的構造函數,析構的時候先調用派生類析構函數再調用基類析構函數。
其實這個很好理解,派生類的成員由兩部分組成,一部分是從基類那里繼承而來,一部分是自己定義的。那么在實例化對象的時候,首先利用基類構造函數去初始化從基類繼承而來的成員,再用派生類構造函數初始化自己定義的部分。
同時,不止構造函數派生類只負責自己的那部分,析構函數也是,所以派生類的析構函數會只析構自己的那部分,這時候如果基類的析構函數不是虛函數,則不能調用基類的析構函數析構從基類繼承來的那部分成員,所以就會出現只刪一半的現象,造成內存泄漏。
所以析構函數要定義成虛函數。
