Effective c++學習筆記——條款07:為多態基類聲明virtual解構函式

來源:互聯網
上載者:User
Declare destructors virtual in polymorphic base classes        1、為什麼要申明虛函數       C++程式設計中通常會存在一個基類有多個衍生類別型,而基類中的存在的大都是純虛函數,需要衍生類別型實現。而這樣的情況下,通過使用factory模式返回一個基底類型的指標。在C++中明確指出,一個衍生類別型經過由一個基底類型指標被刪除,而該基底類型帶著一個non-virtual解構函式其結果未定義。只會造成一個局部的銷毀,即基底類型資源被釋放,而衍生類別型造成memory
leak,看下面的代碼:
// Virtual_Const.cpp : 定義控制台應用程式的進入點。//#include "stdafx.h"#include <iostream>using namespace std;class Base{public:Base(){}~Base(){ cout<<"~Base()"<<endl;}////不帶virtual時輸出的結果請看圖一//virtual ~Base(){ cout<<"~Base()"<<endl;}////正確的做法帶virtual時輸出的結果請看圖二};class Derived:public Base{public:Derived(){p=(char*)malloc(sizeof(char)*10);}~Derived(){free(p);cout<<"~Derived()"<<endl;}private:char *p;};int _tmain(int argc, _TCHAR* argv[]){Base *ptr=new Derived();if (ptr==NULL){cout<<"未分配成功"<<endl;exit(1);}delete ptr;return 0;}
圖一:不帶virtual時輸出結果:圖二:帶virtual時輸出結果:
 以上代碼和輸出結果可以看出,Derived類繼承了Base類,並且在heap上申請了記憶體資源,在它的解構函式中被釋放。但由於Base的解構函式是non-virtual,所以根本沒有正確釋放Derived類型中的ptr指標。 2、virtual 函數實現內部機制                polymorphic(帶多態性質的)base classes應該聲明一個virtual解構函式。如果class帶有任何virtual函數,它就應該擁有一個virtual解構函式。Classes的設計目的如果不是作為base
classes使用,或不是為了具備多態性(polymorphically),就不應該聲明virtual解構函式。那麼是不是所有的class都加上virtual解構函式以保萬一,事實不是如此。如果沒有必要,請不要為解構函式加上virtual二字。原因是virtual是有代價的,為了實現virtual函數,類中間必須要增加一個pointer指向虛函數表,這樣增大了類的體積。所以沒有必要的話,還是不要隨意聲明virtual的解構函式。普遍的規則是只有當類當中有virtual的函數時,解構函式才聲明為virtual。也就是說這個基類是有多態性質(polymorphic)的。        虛函數的實現需要它所在的對象包含額外的資訊,這一資訊用來在運行時確定本對象需要調用哪個虛函數。通常,這一資訊採取一個指標的形式,這個指標被稱為“ vptr ”(“虛函數表指標”)。 vptr 指向一個包含函數指標的數組,這一數組稱為“ vtbl ”(“虛函數表”),每個包含虛函數的類都有一個與之相關的 vtbl 。當一個虛函數被一個對象調用時,就用到了該對象的
vptr 所指向的 vtbl ,在 vtbl 中尋找一個合適的函數指標,然後調用相應的實函數。 請看如下代碼,就知道聲明virtual函數是要浪費系統資源的。

 

// sev_vir.cpp : 定義控制台應用程式的進入點。//#include "stdafx.h"#include <iostream>using namespace std;class class1{};class class2{public:virtual ~ class2();};int _tmain(int argc, _TCHAR* argv[]){    cout<<sizeof(class1)<<endl;    cout<<sizeof(class2)<<endl;    return 0;}

3、請注意

       不過還有一個問題,那就是繼承那些不帶virtual函數的標準類庫的類,比如string類。如果你繼承了string類,那麼當你使用string的指標釋放你的繼承類的話,依然存在上面描述的問題,你的繼承類沒有釋放它該釋放的空間。因為string沒有virtual的解構函式。所以請不要去繼承這些並沒有virtual解構函式的類。

        還有就是STL中的容器通常繼承都毫無意義,因為它們也都沒有提供virtual解構函式。C++並沒有提供像JAVA或者C#的final和sealed這樣的約束關鍵字來禁止某個類型被繼承。 並不是每個類型都會被用來做繼承用途,也不是每個類型設計出來被用來做為多態用途,所以正確的設計相當重要。

4、純虛函數實現

// Pure_vir.cpp : 定義控制台應用程式的進入點。//#include "stdafx.h"#include <iostream>using namespace std;class Base //abstract class{public:    virtual ~Base(){};//virtual, not pure    virtual void func() const = 0;//pure virtual    };void Base::func() const //pure virtual also can have function body{    cout <<"Base::func"<<endl;}class Derived : public Base{public:    Derived(){}    virtual void func() const    {        Base::func();        cout <<"Derived::func"<<endl;    }    virtual void foo(){}};int _tmain(int argc, _TCHAR* argv[]){    Base* pb=new Derived();    pb->func();    pb->Base::func();    return 0;} 
  從產生的結果我們可以看到有純虛函數的類是抽象類別,不能產生對象,只能派生。

        如果他派生的類的純虛函數沒有被改寫,那麼,它的衍生類別還是個抽象類別。定義純虛函數就是為了讓基類不可執行個體化化, 因為執行個體化這樣的抽象資料結構本身並沒有意義。或者給出實現也沒有意義實際上我個人認為純虛函數的引入 ,為了安全.因為避免任何需要明確但是因為不小心而導致的未知的結果,提醒子類去做應做的實現.。

請記住:

1、polymorphic(帶多態性質的) base classes應該聲明一個virtual 解構函式。如果class帶有任何virtual函數,它就應該擁有一個virtual解構函式。

2、Classes的設計目的如果不是作為base classes使用,或不是為了具備多態性質(polymorphically),就不該聲明virtual解構函式。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.