C++虛析構函數

C++虛析構函數主要關注兩個問題,何時要用虛析構以及它是怎麼工做的。下面回答這兩個問題。ios

問題1:何時要用虛析構函數?

答案:經過基類的指針來刪除派生類的對象時,基類的析構函數應該是虛的。這樣作是爲了當用一個基類的指針刪除一個派生類的對象時,派生類的析構函數會被調用。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
複製代碼

問題2:C++中的虛函數是如何工做的?

虛函數依賴虛函數表進行工做。若是一個類中,有函數被關鍵詞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.
複製代碼
相關文章
相關標籤/搜索