析構函數的作用與構造函數正好相反,是在對象的生命期結束時,釋放系統為對象所分配的空間,即要撤消一個對象。
用對象指針來調用一個函數,有以下兩種情況:
-
如果是虛函數,會調用派生類中的版本。(在有派生類的情況下)
-
如果是非虛函數,會調用指針所指類型的實現版本。
析構函數也會遵循以上兩種情況,因為析構函數也是函數嘛,不要把它看得太特殊。 當對象出了作用域或是我們刪除對象指針,析構函數就會被調用。
當派生類對象出了作用域,派生類的析構函數會先調用,然后再調用它父類的析構函數, 這樣能保證分配給對象的內存得到正確釋放。
但是,如果我們刪除一個指向派生類對象的基類指針,而基類析構函數又是非虛的話, 那么就會先調用基類的析構函數(上面第2種情況),派生類的析構函數得不到調用。
請看例子:

#include<stdio.h> #include<iostream> class A{ public:A(); virtual~A(); virtual void fun1(){ printf("123"); } }; A::A(){} A::~A(){ printf("Delete class A\n"); } class B : public A { public:B(); ~B(); void fun2(){ printf("123456"); } }; B::B(){ } B::~B(){ printf("Delete class B\n"); } A *a=new B; //B *b=new A; //無法從“A *”轉換為“B *” B *c=new B; A *d=new A; int main(){ delete a; //delete c; //delete d; return 0; }
析構函數加上虛函數,當你動態申請一個對象時,並且把這個對象的指針賦值給基類,這時當你用這個基類指針釋放內存時,就有用了,因為這樣可以用多態性原理調用對象實際的析構函數來析構內存。

#include<stdio.h> #include<iostream> class A{ public:A(); ~A(); virtual void fun1(){ printf("123"); } }; A::A(){} A::~A(){ printf("Delete class A\n"); } class B : public A { public:B(); ~B(); void fun2(){ printf("123456"); } }; B::B(){ } B::~B(){ printf("Delete class B\n"); } A *a=new B; //B *b=new A; //無法從“A *”轉換為“B *” B *c=new B; A *d=new A; int main(){ delete a; //delete c; //delete d; return 0; }
析構函數去掉虛函數,就不能調用子類中的析構函數了
當你動態申請一個對象時,並且把這個對象的指針賦值給當前類,析構函數去掉虛函數,都不會影響子類到父類的虛構

#include<stdio.h> #include<iostream> class A{ public:A(); ~A(); virtual void fun1(){ printf("123"); } }; A::A(){} A::~A(){ printf("Delete class A\n"); } class B : public A { public:B(); ~B(); void fun2(){ printf("123456"); } }; B::B(){ } B::~B(){ printf("Delete class B\n"); } A *a=new B; //B *b=new A; //無法從“A *”轉換為“B *” B *c=new B; A *d=new A; int main(){ delete c; //delete c; //delete d; return 0; }