C++虛析構函數主要關注兩個問題,何時要用虛析構以及它是怎麼工做的。下面回答這兩個問題。ios
答案:經過基類的指針來刪除派生類的對象時,基類的析構函數應該是虛的。這樣作是爲了當用一個基類的指針刪除一個派生類的對象時,派生類的析構函數會被調用。c++
緣由:用對象指針來調用一個函數,有如下兩種狀況:若是是虛函數,會調用派生類中的版本; 若是是非虛函數,會調用指針所指類型的實現版本。 析構函數也會遵循以上兩種狀況。當對象出了做用域或是咱們刪除對象指針,析構函數就會被調用。當派生類對象出了做用域,派生類的析構函數會先調用,而後再調用它父類的析構函數,這樣能保證分配給對象的內存獲得正確釋放。可是,若是咱們刪除一個指向派生類對象的基類指針,而基類析構函數又是非虛的話, 那麼就會先調用基類的析構函數(上面第2種狀況),派生類的析構函數得不到調用。這樣會形成銷燬對象不徹底。bash
簡要解釋就是:析構函數執行時先調用派生類的析構函數,其次才調用基類的析構函數。若是析構函數不是虛函數,而程序執行時又要經過基類的指針去銷燬派生類的動態對象,那麼用delete銷燬對象時,只調用了基類的析構函數,未調用派生類的析構函數。這樣會形成銷燬對象不徹底。函數
注意:並非要把全部類的析構函數都寫成虛函數。由於當類裏面有虛函數的時候,編譯器會給類添加一個虛函數表,裏面來存放虛函數指針,這樣就會增長類的存儲空間。因此,只有當一個類被用來做爲基類的時候,才把析構函數寫成虛函數。ui
代碼實例以下:this
class Base{
public:
Base() { cout<<"Base Constructor"<<endl; }
// ~Base() { cout<<"Base Destructor"<<endl; }
virtual ~Base() { cout<<"Base Destructor"<<endl; }
};
class Derived: public Base{
public:
Derived() { cout<<"Derived Constructor"<<endl; }
~Derived() { cout<<"Derived Destructor"<<endl; }
};
int main(){
Base *p = new Derived();
delete p;
return 0;
}
複製代碼
未添加虛析構函數輸出:spa
BaseConstructor
DerivedConstructor
BaseDestructor
複製代碼
添加虛析構函數輸出:指針
BaseConstructor
DerivedConstructor
DerivedDestructor
BaseDestructor
複製代碼
虛函數依賴虛函數表進行工做。若是一個類中,有函數被關鍵詞virtual進行修飾, 那麼一個虛函數表就會被構建起來保存這個類中虛函數的地址。同時,編譯器會爲這個類添加一個隱藏指針(虛函數表的指針)指向虛函數表。若是在派生類中沒有重寫虛函數,那麼,派生類中虛表存儲的是父類虛函數的地址。每當虛函數被調用時, 虛表會決定具體去調用哪一個函數。所以,C++中的動態綁定是經過虛函數表機制進行的。當咱們用基類指針指向派生類時,虛表指針vptr指向派生類的虛函數表。 這個機制能夠保證派生類中的虛函數被調用到。code
示例代碼:對象
#include <iostream>
using namespace std;
class Shape{
public:
Shape(){}
Shape(int edge_length){
this->edge_length = edge_length;
}
virtual ~Shape(){
cout<<"Shape destructure."<<endl;
}
virtual int circumstance(){
cout<<"circumstance of base class."<<endl;
return 0;
}
protected:
int edge_length;
};
class Triangle: public Shape{
public:
Triangle(){}
Triangle(int edge_length){
this->edge_length = edge_length;
}
~Triangle(){
cout<<"Triangle destructure."<<endl;
}
int circumstance(){
cout<<"circumstance of child class."<<endl;
return 3 * this->edge_length;
}
};
int main() {
Shape *x = new Shape();
x->circumstance();
Shape *y = new Triangle(10);
int num = y->circumstance();
cout<<num<<endl;
delete x;
delete y;
return 0;
}
複製代碼
運行結果:
circumstance of base class.
circumstance of child class.
30
Shape destructure.
Triangle destructure.
Shape destructure.
複製代碼