程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++虛析構函數、虛函數結合考題變種

C++虛析構函數、虛函數結合考題變種

編輯:C++入門知識

虛析構函數、虛函數結合考題變種
  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。
         即為類析構函數聲明為虛析構函數是以付出內存為代價的。所以,無端將所有類的析構函數聲明為虛函數,就向從未聲明它們是虛函數一樣,都是錯誤的。關注盛世游戲http://www.shengshiyouxi.com
  
  總結如下:
  (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,且調用本層的foo
private:
       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. 下一頁:
Copyright © 程式師世界 All Rights Reserved