1. 程式人生 > >C/C++基類的解構函式為什麼必須定義為虛擬函式?

C/C++基類的解構函式為什麼必須定義為虛擬函式?

C/C++基類的解構函式為什麼必須定義為虛擬函式?

 

為什麼基類的解構函式是虛擬函式?

在實現多型時,當用基類操作派生類,在析構時防止只析構基類而不析構派生類的狀況發生。

(1)第一種情況:沒有多型,建立派生類物件,基類的解構函式不是虛擬函式

#include<iostream>
using namespace std;
//基類
class ClxBase{
public:
    ClxBase() {};
    //解構函式不是虛擬函式
    ~ClxBase() {
        cout << "Output from the destructor of class ClxBase!" << endl;
    };

    void DoSomething() {
        cout << "Do something in class ClxBase!" << endl; 
    };
};

//派生類
class ClxDerived : public ClxBase{
public:
    ClxDerived() {};
    ~ClxDerived() { 
        cout << "Output from the destructor of class ClxDerived!" << endl; 
    };

    void DoSomething() { 
        cout << "Do something in class ClxDerived!" << endl;
    };
};

  int main(){  
      //沒有多型,派生類物件
      ClxDerived *p =  new ClxDerived;
      p->DoSomething();
      delete p;
      return 0;
  }

//執行結果
Do something in class ClxDerived!            
Output from the destructor of class ClxDerived!
Output from the destructor of class ClxBase!  

這段程式碼中基類的解構函式不是虛擬函式,在main函式中用派生類的指標去操作派生類的成員。釋放指標P的過程是:先釋放派生類的資源(執行~ClxDerived()),再釋放基類資源(C++ primer裡說編譯器總是顯示呼叫派生類物件基類部分的解構函式~ClxBase())。

(2)第二種情況:有多型,建立派生類物件,基類的解構函式不是虛擬函式

#include<iostream>
using namespace std;
//基類
class ClxBase{
public:
    ClxBase() {};
    //解構函式不是虛擬函式
    ~ClxBase() {
        cout << "Output from the destructor of class ClxBase!" << endl;
    };

    void DoSomething() {
        cout << "Do something in class ClxBase!" << endl;
    };
};

//派生類
class ClxDerived : public ClxBase{
public:
    ClxDerived() {};
    ~ClxDerived() {
        cout << "Output from the destructor of class ClxDerived!" << endl;
    };

    void DoSomething() {
        cout << "Do something in class ClxDerived!" << endl;
    }
};
  int main(){ 
      //有多型
      ClxBase *p =  new ClxDerived;
      p->DoSomething();
      delete p;
      return 0;
  } 

//執行結果
Do something in class ClxBase!
Output from the destructor of class ClxBase!

這段程式碼中基類的解構函式同樣不是虛擬函式,不同的是在main函式中用基類的指標去操作派生類的成員。釋放指標P的過程是:只是釋放了基類的資源,而沒有呼叫派生類的解構函式(因沒有多型,靜態繫結,只執行基類的解構函式~ClxBase())。呼叫dosomething()函式執行的也是基類定義的函式。

一般情況下,這樣的刪除只能夠刪除基類物件,而不能刪除子類物件,形成了刪除一半的現象從而造成記憶體洩漏。正因為如此,需要將基類的解構函式定義為虛解構函式!!

在公有繼承中,基類對派生類及其物件的操作,只能影響到那些從基類繼承下來的成員。如果想要用基類對非繼承成員進行操作,則要把基類的這個函式定義為虛擬函式。 
解構函式自然也應該如此:如果它想析構子類中的重新定義或新的成員及物件,當然也應該宣告為虛的。

(3)第三種情況:有多型,建立派生類物件,基類的解構函式是虛擬函式

#include<iostream>
using namespace std;

//基類
class ClxBase{
public:
    ClxBase() {};
    virtual ~ClxBase() {
        cout << "Output from the destructor of class ClxBase!" << endl;
    };
    virtual void DoSomething() { 
        cout << "Do something in class ClxBase!" << endl;
    };
};

//派生類
class ClxDerived : public ClxBase{
public:
    ClxDerived() {};
    ~ClxDerived() { 
        cout << "Output from the destructor of class ClxDerived!" << endl;
    };
    void DoSomething() {
        cout << "Do something in class ClxDerived!" << endl;
    };
};

  int main(){ 
      //有多型
      ClxBase *p =  new ClxDerived;
      //當基類是虛擬函式時,基類的指標將表現為派生類的行為(非虛擬函式將表現為基類行為)
      p->DoSomething();
      delete p;
      return 0;
  }

//執行結果
Do something in class ClxDerived!
Output from the destructor of class ClxDerived!
Output from the destructor of class ClxBase!

這段程式碼中基類的解構函式被定義為虛擬函式,在main函式中用基類的指標去操作派生類的成員。釋放指標P的過程是:先釋放了繼承類的資源,再呼叫基類的解構函式。呼叫dosomething()函式執行的也是繼承類定義的函式。