虛解構函式、虛函數結合考題變種

來源:互聯網
上載者:User
虛解構函式、虛函數結合考題變種

1.[Effective C++原則07]:為多態基類聲明virtual 解構函式。

[如果不]: 如果不聲明為解構函式,可能出現的結果如下:Derived對象的成分沒有被銷毀,形成資源流失、在調試上會浪費很長時間。

class CSimpleClass{public:CSimpleClass(){ cout << "CSimpleClass" <<endl; }~CSimpleClass() { cout <<"~CSimpleClass" << endl; }private:}; class CDerived : public CSimpleClass{public:CDerived() { cout << "CDerived" << endl; }~CDerived() { cout << "~CDerived" << endl; }private:}; int main(){CSimpleClass *pSimple = new CDerived;delete pSimple; return 0;}

執行結果如下:


顯然,CDerived 對象沒有被析構!

1、
造成上述不同的原因何在?

“C++標準”明確指出,當衍生類別對象經由一個基類指標pBaseObject被刪除,而該基類帶有一個non-virtual解構函式,其結果未有定義(即不可預知)。實際執行時,如上面第一個圖示,會產生bug,衍生類別的對象沒有被銷毀。

這就形成詭異的“局部銷毀”對象,形成資源流失。

 

2、
什麼時候需要基類解構函式聲明為虛函數?什麼時候不需要基類的解構函式為虛函數?

       該問題涉及解構函式何時應該為虛函數。注意:對於上面的基類BaseClass,

       若解構函式不為虛函數,sizeof(BaseClass) = 1。

       若解構函式為虛函數,sizeof(BaseClass) = 4。

       至於為什麼包含建構函式、非虛解構函式的類的大小為1個位元組。解釋如下:

       空類類的大小比如BaseClass沒有構造、解構函式,本來sizeof(BaseClass)應該為0,但是我們聲明該類型執行個體的時候,必須在記憶體中佔用一定的空間,否則無法使用這些執行個體。至於佔用多少記憶體,由編譯器決定,visual studio中每個空類型的執行個體佔用1個位元組的空間。

       而加上建構函式、解構函式或其他非虛類型的函數以後呢?由於這些非虛類型的函數的地址只與類有關,而與類的執行個體無關,編譯器不會因為非虛函數的增加而添加任何額外的資訊。

       那麼為什麼解構函式變成虛函數後,大小就變成4個位元組了呢?主要原因是:C++一旦發現類中有虛函數,就會為該類產生虛函數表,並在該類型的每一個執行個體中添加指向虛函數表的指標。在32位機器上,一個指標佔4個位元組的空間,所以求sizeof大小為4。而在64位機器上,一個指標佔用8個位元組的空間,因此sizeof大小為8。

       即為類解構函式聲明為虛解構函式是以付出記憶體為代價的。所以,無端將所有類的解構函式聲明為虛函數,就向從未聲明它們是虛函數一樣,都是錯誤的。

 

總結如下:

(1)帶多態性質的基類應用聲明一個虛解構函式。如果類中帶有任何虛函數,它就應該擁有一個虛解構函式;

(2)設計類的目的如果不作為基類,或者不是為了具備多態性,就不應該聲明虛解構函式。

——參考《Effective C++》條款7;《劍指Offer》

 

2.[Effective 原則09]:絕不在構造和析構過程中調用virtual函數。

【原因】:base class的執行更早於derived class的建構函式,當base class的建構函式執行的時候derived class的成員變數尚未初始化。

【如果不】:執行的結果不會動態聯編,依然執行其所在層的虛函數。

【樣本如下】:

class CSimpleClass{public:       CSimpleClass() { cout << "CSimpleClass"<< endl; foo();} //調用了本層的foo       virtual ~CSimpleClass() { cout <<"~CSimpleClass" << endl; foo();} //調用了本層的foo       virtual void foo() { cout << "CSimpleClass::foo()" << endl; }private:}; classCDerived : public CSimpleClass{public:       CDerived() { cout <<"CDerived" << endl; foo(); }       ~CDerived() { cout <<"~CDerived" << endl; foo(); }       void foo() { cout<< "CDerived::foo()" << endl; }private:}; int main(){       CSimpleClass *pSimple = new CDerived;       delete pSimple;        return 0;}

執行結果如下:


3.綜合1,2的筆試題如下:

class CBase{public:       CBase(){ cout << "CBase ctor" << endl; foo(); }    //調用本層的foo        ~CBase() { cout<< "CBase dtor" << endl; foo(); }  //未加virtual,且調用本層的fooprivate:       virtualvoid foo(){ cout << "Base::foo()" << endl; } //}; class CDerived : public  CBase{public:       CDerived(){cout << "CDerived ctor" << endl; foo(); }       ~CDerived(){cout << "CDerived dtor" << endl; foo(); } private:       virtual void foo(){ cout << "Derived::foo()" << endl; }}; int main(){       CBase*pBase = new CDerived;       delete pBase;        return 0;}

結合原則1,2.正確的輸出結果是:


顯然,1.CDerived的解構函式不會被調用,因為CBase的解構函式非虛函數。

2.在CBase的構造和解構函式中調用虛函數,僅會執行本層的定義,不會下調。還是遵照[Effective 原則09]:絕不在構造和析構過程中調用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.