C++: 析構函數爲什麼要爲虛函數

  基類指針或引用可以指向或是引用派生類對象,下文以指針爲例作講解。引用的例子類似,不再贅述。
  如果基類指針向派生類對象,則刪除此指針時,我們希望調用該指針指向的派生類析構函數,而派生類的析構函數又自動調用基類的析構函數,這樣整個派生類的對象完全被釋放。
  若使用基類指針操作派生類,需要防止在析構時,只析構基類,而不析構派生類。
  但是,如果析構函數不被聲明成虛函數,則編譯器採用的綁定方式是靜態綁定,在刪除基類指針時,只會調用基類析構函數,而不調用派生類析構函數,這樣就會導致基類指針指向的派生類對象析構不完全。若是將析構函數聲明爲虛函數,則可以解決此問題。
比較以下三個例子:
1、第一段代碼

#include<iostream>
using namespace std;
class BC{
public:
    BC () { strsBC = new string[10]; cout << "BC::BC()!" << endl;};
    ~BC() {delete [] strsBC; cout << "free strsBC in BC::~BC()!" << endl;};
void fun1() { cout << "BC::fun1()!" << endl; };
private:
    string * strsBC;
};

class DC : public BC{
public:
    DC () {pa = new int[6]; cout << "DC::DC()!" << endl; };
    ~DC () {delete [] pa; cout << "free pa in DC::~DC()!" << endl; };
    void fun1() { cout << "DC::fun1()!" << endl; };
private:
    int *pa;
};
int main(){  
  DC *p =  new DC();
  p->fun1();
  delete p;
  return 0;
}

運行結果:
BC::BC()!
DC::DC()!
DC::fun1()!
free pa in DC::~DC()!
free strsBC in BC::~BC()!
  這段代碼中,基類析構函數不是虛函數。在main函數中,用子類指針操作子類。釋放指針P的過程是:先釋放繼承類的資源,再釋放基類資源,則釋放是完全的,不存在問題。

2、第二段代碼

#include<iostream>
using namespace std;
class BC{
public:
    BC () { strsBC =new string[10]; cout << "BC::BC()!" << endl;};
    ~BC() {delete [] strsBC; cout << "free strsBC in BC::~BC()!" << endl;};
void fun1() { cout << "BC::fun1()!" << endl; };
private:
    string * strsBC;
};

class DC : public BC{
public:
    DC () {pa = new int[6]; cout << "DC::DC()!" << endl; };
    ~DC () {delete [] pa; cout << "free pa in DC::~DC()!" << endl; };
    void fun1() { cout << "DC::fun1()!" << endl; };
private:
    int *pa;
};
int main(){  
  BC *p =  new DC;
  p->fun1();
  delete p;
  return 0;
}

輸出結果:
BC::BC()!
DC::DC()!
BC::fun1()!
free strsBC in BC::~BC()!
  這段代碼中,基類析構函數不是虛函數,但在main函數中,用基類指針操作子類,釋放指針P的過程是:只是釋放了基類申請的資源,而沒有調用子類的析構函數,沒有釋放了子類申請的資源。調用fun1()函數,執行的也是基類定義的函數。
  一般情況下,這樣的刪除只能夠刪除基類對象,而不能刪除子類對象,形成了刪除一半形象,造成內存泄漏。
  要想解決上述問題,需要聲明析構函數是虛函數。

3、第三段代碼:

#include<iostream>
using namespace std;
class BC{
public:
    BC () { strsBC =new string[10]; cout << "BC::BC()!" << endl;};
    virtual ~BC() {delete [] strsBC; cout << "free strsBC in BC::~BC()!" << endl;};
virtual void fun1() { cout << "BC::fun1()!" << endl; };
private:
    string * strsBC;
};

class DC : public BC{
public:
    DC () {pa = new int[6]; cout << "DC::DC()!" << endl; };
    ~DC () {delete [] pa; cout << "free pa in DC::~DC()!" << endl; };
    void fun1() { cout << "DC::fun1()!" << endl; };
private:
    int *pa;
};
int main(){  
  BC *p =  new DC;
  p->fun1();
  delete p;
  return 0;
}

運行結果:
BC::BC()!
DC::DC()!
DC::fun1()!
free pa in DC::~DC()!
free strsBC in BC::~BC()!

  這段代碼中,基類析構函數是虛函數。在main函數中,用基類指針操作子類,釋放指針P的過程是:先釋放子類申請的資源,再調用基類析構函數,釋放父類申請的資源。調用fun1()函數,執行的也是子類中定的函數,表現出了多態特徵。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章