一、當(dāng)你的類準(zhǔn)備給別人繼承時要提供虛析構(gòu)函數(shù) 考慮下面例子: class A { public: A(){cout << "In A constructor" << endl;} ~A(){cout << "In A destructor" << endl;} };
class B : public A { public: B() { cout << "In B constructor" << endl; m_p = new char[10]; } ~B() { cout << "In B destructor" << endl; if (m_p) delete [] m_p; } private: char *m_p; };
int main(int argc, char* argv[]) { //printf("Hello World!/n"); A *p = new B; delete p; return 0; } 輸出結(jié)果: In A constructor In B constructor In A destructor
并沒有調(diào)用B的析構(gòu)函數(shù),new出來的內(nèi)存沒有及時回收造成內(nèi)存泄漏。 要解決這個問題,只要將A的析構(gòu)函數(shù)定義為虛函數(shù):~A(){cout << "In A destructor" << endl;}。為什么定義為虛函數(shù)就能解決呢?我是這樣理解的: 象其它虛構(gòu)函數(shù)一樣,~B()重定義(overridden)了~A(),這樣指向派生類的指針就能根據(jù)運行時的狀態(tài)調(diào)用B的析構(gòu)函數(shù)了。這里又有一個問題:為什么還會調(diào)用A的析構(gòu)函數(shù)呢?我只能理解為析構(gòu)函數(shù)是一個特殊的函數(shù),由系統(tǒng)維護(hù)其機制。就像B.~A()是錯誤而B.~B()(雖然邏輯上不對,但語法上是正確的,編譯運行完全沒問題)是正確的一樣。 虛函數(shù)和普通成員函數(shù)的區(qū)別,是虛函數(shù)放在虛函數(shù)表中,通過對象的this指針找到該類的虛函數(shù)表,然后調(diào)用。C++即采用此機制實現(xiàn)多態(tài)。如果是普通函數(shù),每個函數(shù)的地址是死的。所以用A類的對象調(diào)用析構(gòu)函數(shù)時只能調(diào)到A的析構(gòu)。如果是虛函數(shù),則會通過指針找到B的析構(gòu)函數(shù),而B繼承自A,還會調(diào)用A的析構(gòu)函數(shù)。
因此,虛析構(gòu)函數(shù)用在有虛函數(shù)的繼承。否則,你那樣的用法也是不正確的。A *p=new B 如果沒有虛函數(shù),會導(dǎo)致截斷。
二、 //////////////////////////////////////////////////////////////////////////////// #if CODE1 #include <iostream> //給出一個沒有虛擬析構(gòu)函數(shù)的基類 class Base { public: Base() { std::cout<<"Base::Base()"<<std::endl; } ~Base() { std::cout<<"Base::~Base()"<<std::endl; } }; //給出一個沒有虛擬析構(gòu)函數(shù)的繼承類 class Derived:public Base { public: Derived() { std::cout<<"Derived::Derived()"<<std::endl; } ~Derived() { std::cout<<"Derived::~Derived()"<<std::endl; } }; //下面的測試代碼 int main() { {//堆棧變量的情況 std::cout << "----------[Derived d;]----------" << std::endl; Derived d; } {//堆內(nèi)存分配,并且刪除派生類對象指針的情況 std::cout << "----------[Derived *pd = new Derived();]----------" << std::endl; Derived *pd = new Derived(); std::cout << "----------[delete pd;]----------" << std::endl; delete pd;//這里會調(diào)用派生類和基類的析構(gòu)函數(shù),雖然派生類和基類的析構(gòu)函數(shù)不是虛擬的 } {//堆內(nèi)存分配,并且刪除基類對象指針的情況 std::cout << "----------[Base *pb = new Derived();]----------" << std::endl; Base *pb = new Derived(); std::cout << "----------[delete pb;]----------" << std::endl; delete pb;//這里就不會調(diào)用派生類的析構(gòu)函數(shù) } return 0; } //////////////////////////////////////////////////////////////////////////////// //運行結(jié)果如下所示: /******************************************************************************* ----------[Derived d;]---------- Base::Base() Derived::Derived() Derived::~Derived() Base::~Base() ----------[Derived *pd = new Derived();]---------- Base::Base() Derived::Derived() ----------[delete pd;]---------- Derived::~Derived() Base::~Base() ----------[Base *pb = new Derived();]---------- Base::Base() Derived::Derived() ----------[delete pb;]---------- Base::~Base() *******************************************************************************/ //////////////////////////////////////////////////////////////////////////////// #if 0 從上面的討論中可以看出,只有最后一種情況才會對析構(gòu)函數(shù)是否是虛擬的有要求,在 后面的討論中將會只討論這種情況。 #endif #endif//CODE1 #if CODE2 #include <iostream> //給出一個沒有虛擬析構(gòu)函數(shù)的基類 class Base { public: Base() { std::cout<<"Base::Base()"<<std::endl; } ~Base() { std::cout<<"Base::~Base()"<<std::endl; } }; //給出一個有虛擬析構(gòu)函數(shù)的繼承類 class Derived:public Base { public: Derived() { std::cout<<"Derived::Derived()"<<std::endl; } virtual~Derived() { std::cout<<"Derived::virtual~Derived()"<<std::endl; } }; //下面的測試代碼 int main() { //堆內(nèi)存分配,并且刪除基類對象指針的情況 std::cout << "----------[Base *pb = new Derived();]----------" << std::endl; Base *pb = new Derived(); std::cout << "----------[delete pb;]----------" << std::endl; delete pb;//這里就不會調(diào)用派生類的析構(gòu)函數(shù) return 0; } //////////////////////////////////////////////////////////////////////////////// //運行結(jié)果如下所示: /******************************************************************************* ----------[Base *pb = new Derived();]---------- Base::Base() Derived::Derived() ----------[delete pb;]---------- Base::~Base() *******************************************************************************/ //////////////////////////////////////////////////////////////////////////////// #if 0 很明顯并沒有調(diào)用派生類的虛擬析構(gòu)函數(shù)。 #endif #endif//CODE2 #if CODE3 #include <iostream> //給出一個有虛擬析構(gòu)函數(shù)的基類 class Base { public: Base() { std::cout<<"Base::Base()"<<std::endl; } virtual~Base() { std::cout<<"Base::virtual~Base()"<<std::endl; } }; //給出一個沒有虛擬析構(gòu)函數(shù)的繼承類 class Derived:public Base { public: Derived() { std::cout<<"Derived::Derived()"<<std::endl; } ~Derived() { std::cout<<"Derived::~Derived()"<<std::endl; } }; //下面的測試代碼 int main() { //堆內(nèi)存分配,并且刪除基類對象指針的情況 std::cout << "----------[Base *pb = new Derived();]----------" << std::endl; Base *pb = new Derived(); std::cout << "----------[delete pb;]----------" << std::endl; delete pb;//這里會調(diào)用派生類的析構(gòu)函數(shù) return 0; } //////////////////////////////////////////////////////////////////////////////// //運行結(jié)果如下所示: /******************************************************************************* ----------[Base *pb = new Derived();]---------- Base::Base() Derived::Derived() ----------[delete pb;]---------- Derived::~Derived() Base::virtual~Base() *******************************************************************************/ //////////////////////////////////////////////////////////////////////////////// #if 0 很明顯調(diào)用派生類的虛擬析構(gòu)函數(shù)。但是這里值得注意的是:派生類的析構(gòu)函數(shù)并不 是虛擬的,為了說明派生類的虛擬析構(gòu)函數(shù)是否一定要是虛擬的,還需要更多的測試。 #endif #endif//CODE3 #if CODE4 #include <iostream> //給出一個有虛擬析構(gòu)函數(shù)的基類 class Base { public: Base() { std::cout<<"Base::Base()"<<std::endl; } virtual~Base() { std::cout<<"Base::virtual~Base()"<<std::endl; } }; //給出一個有虛擬析構(gòu)函數(shù)的繼承類 class Derived:public Base { public: Derived() { std::cout<<"Derived::Derived()"<<std::endl; } virtual~Derived() { std::cout<<"Derived::virtual~Derived()"<<std::endl; } }; //下面的測試代碼 int main() { //堆內(nèi)存分配,并且刪除基類對象指針的情況 std::cout << "----------[Base *pb = new Derived();]----------" << std::endl; Base *pb = new Derived(); std::cout << "----------[delete pb;]----------" << std::endl; delete pb;//這里會調(diào)用派生類的析構(gòu)函數(shù) return 0; } //////////////////////////////////////////////////////////////////////////////// //運行結(jié)果如下所示: /******************************************************************************* ----------[Base *pb = new Derived();]---------- Base::Base() Derived::Derived() ----------[delete pb;]---------- Derived::virtual~Derived() Base::virtual~Base() *******************************************************************************/ //////////////////////////////////////////////////////////////////////////////// #if 0 很明顯調(diào)用派生類的虛擬析構(gòu)函數(shù)。通過CODE3和CODE4可以看出只要基類的析構(gòu)函數(shù) 是虛擬的則派生類的析構(gòu)函數(shù)不論是否是虛擬的都將在刪除基類指針的時候調(diào)用派生類的 析構(gòu)函數(shù)。不過CODE3和CODE4僅僅討論的是一次一重繼承的情況。對于多次多重繼承的情 況則需要另外的單獨討論。還是先討論多次繼承的情況,然后再討論多重繼承的情況,最 后討論多次多重繼承的情況。 #endif #endif//CODE4 #if CODE5 //測試多次繼承的情況 #include <iostream> //給出一個有虛擬析構(gòu)函數(shù)的基類 class Base { public: Base() { std::cout<<"Base::Base()"<<std::endl; } virtual~Base() { std::cout<<"Base::virtual~Base()"<<std::endl; } }; //給出一個沒有虛擬析構(gòu)函數(shù)的繼承類 class Derived1:public Base { public: Derived1() { std::cout<<"Derived1::Derived1()"<<std::endl; } ~Derived1() { std::cout<<"Derived1::~Derived1()"<<std::endl; } }; //給出另一個沒有虛擬析構(gòu)函數(shù)的繼承類 class Derived2:public Derived1 { public: Derived2() { std::cout<<"Derived2::Derived2()"<<std::endl; } ~Derived2() { std::cout<<"Derived2::~Derived2()"<<std::endl; } }; //下面的測試代碼 int main() { //堆內(nèi)存分配,并且刪除基類對象指針的情況 { std::cout << "----------[Base *pb = new Derived2();]----------" << std::endl; Base *pb = new Derived2(); std::cout << "----------[delete pb;]----------" << std::endl; delete pb;//這里會調(diào)用派生類的析構(gòu)函數(shù) } { std::cout << "----------[Base *pd1 = new Derived1();]----------" << std::endl; Derived1 *pd1 = new Derived2(); std::cout << "----------[delete pd1;]----------" << std::endl; delete pd1;//這里會調(diào)用派生類的析構(gòu)函數(shù) } return 0; } //////////////////////////////////////////////////////////////////////////////// //運行結(jié)果如下所示: /******************************************************************************* ----------[Base *pb = new Derived2();]---------- Base::Base() Derived1::Derived1() Derived2::Derived2() ----------[delete pb;]---------- Derived2::~Derived2() Derived1::~Derived1() Base::virtual~Base() ----------[Base *pd1 = new Derived1();]---------- Base::Base() Derived1::Derived1() Derived2::Derived2() ----------[delete pd1;]---------- Derived2::~Derived2() Derived1::~Derived1() Base::virtual~Base() *******************************************************************************/ //////////////////////////////////////////////////////////////////////////////// #if 0 很明顯調(diào)用了所有派生類的虛擬析構(gòu)函數(shù)。但是這里值得注意的是:派生類的析構(gòu)函 數(shù)并不是虛擬的??梢钥闯鲋灰谢惖奈鰳?gòu)函數(shù)是虛擬的,那么所有的派生類不管是否 明確的寫了虛擬析構(gòu)函數(shù),派生了的析構(gòu)函數(shù)一定是虛擬的。 #endif #endif//CODE5 #if CODE6 //測試多重繼承的情況 #include <iostream> //給出一個有虛擬析構(gòu)函數(shù)的基類 class Base1 { public: Base1() { std::cout<<"Base1::Base1()"<<std::endl; } virtual~Base1() { std::cout<<"Base1::virtual~Base1()"<<std::endl; } }; class Base2 { public: Base2() { std::cout<<"Base2::Base2()"<<std::endl; } virtual~Base2() { std::cout<<"Base2::virtual~Base2()"<<std::endl; } }; //給出一個沒有虛擬析構(gòu)函數(shù)的繼承類 class Derived:public Base1,public Base2 { public: Derived() { std::cout<<"Derived::Derived()"<<std::endl; } ~Derived() { std::cout<<"Derived::~Derived()"<<std::endl; } }; //下面的測試代碼 int main() { //堆內(nèi)存分配,并且刪除基類對象指針的情況 { std::cout << "----------[Base *pb = new Derived();]----------" << std::endl; Base1 *pb = new Derived(); std::cout << "----------[delete pb;]----------" << std::endl; delete pb;//這里會調(diào)用派生類的析構(gòu)函數(shù) } { std::cout << "----------[Base *pb = new Derived();]----------" << std::endl; Base2 *pb = new Derived(); std::cout << "----------[delete pb;]----------" << std::endl; delete pb;//這里會調(diào)用派生類的析構(gòu)函數(shù) } return 0; } //////////////////////////////////////////////////////////////////////////////// //運行結(jié)果如下所示: /******************************************************************************* ----------[Base *pb = new Derived();]---------- Base1::Base1() Base2::Base2() Derived::Derived() ----------[delete pb;]---------- Derived::~Derived() Base2::virtual~Base2() Base1::virtual~Base1() ----------[Base *pb = new Derived();]---------- Base1::Base1() Base2::Base2() Derived::Derived() ----------[delete pb;]---------- Derived::~Derived() Base2::virtual~Base2() Base1::virtual~Base1() *******************************************************************************/ //////////////////////////////////////////////////////////////////////////////// #if 0 很明顯調(diào)用了所有派生類的析構(gòu)函數(shù)和基類的析構(gòu)函數(shù)。另外還可以看出只要有了多重 繼承的任意一個基類的指針都可以通過這個指針將整個對象刪除。另外還需要考慮一下某個 基類的析構(gòu)函數(shù)不是虛擬的情況。 #endif #endif//CODE6 #if CODE7 //測試多重繼承的情況 #include <iostream> //給出一個有虛擬析構(gòu)函數(shù)的基類 class Base1 { public: Base1() { std::cout<<"Base1::Base1()"<<std::endl; } virtual~Base1() { std::cout<<"Base1::virtual~Base1()"<<std::endl; } }; class Base2 { public: Base2() { std::cout<<"Base2::Base2()"<<std::endl; } ~Base2() { std::cout<<"Base2::~Base2()"<<std::endl; } }; //給出一個沒有虛擬析構(gòu)函數(shù)的繼承類 class Derived:public Base1,public Base2 { public: Derived() { std::cout<<"Derived::Derived()"<<std::endl; } ~Derived() { std::cout<<"Derived::~Derived()"<<std::endl; } }; //下面的測試代碼 int main() { //堆內(nèi)存分配,并且刪除基類對象指針的情況 { std::cout << "----------[Base *pb = new Derived();]----------" << std::endl; Base1 *pb = new Derived(); std::cout << "----------[delete pb;]----------" << std::endl; delete pb;//這里會調(diào)用派生類的析構(gòu)函數(shù) } { std::cout << "----------[Base *pb = new Derived();]----------" << std::endl; Base2 *pb = new Derived(); std::cout << "----------[delete pb;]----------" << std::endl; delete pb;//這里不會調(diào)用派生類的析構(gòu)函數(shù) } return 0; } //////////////////////////////////////////////////////////////////////////////// //運行結(jié)果如下所示: /******************************************************************************* ----------[Base *pb = new Derived();]---------- Base1::Base1() Base2::Base2() Derived::Derived() ----------[delete pb;]---------- Derived::~Derived() Base2::~Base2() Base1::virtual~Base1() ----------[Base *pb = new Derived();]---------- Base1::Base1() Base2::Base2() Derived::Derived() ----------[delete pb;]---------- Base2::~Base2() *******************************************************************************/ //////////////////////////////////////////////////////////////////////////////// #if 0 很明顯,是否調(diào)用所有派生類的析構(gòu)函數(shù)和基類的析構(gòu)函數(shù),取決于刪除的基類指針的 基類的析構(gòu)函數(shù)是否是虛擬的,如果指針的析構(gòu)函數(shù)是虛擬的,那么將會調(diào)用所有的派生類 的析構(gòu)函數(shù)和所有的其它基類的析構(gòu)函數(shù),否則就會象上面的第二種情況一樣僅僅刪除了基 類自身而已,導(dǎo)致對象沒有完全釋放。 #endif #endif//CODE7
|