1. 程式人生 > >虛解構函式、虛擬函式考題

虛解構函式、虛擬函式考題

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

1.[Effective C++原則07]:為多型基類宣告virtual 解構函式。 [如果不]: 如果不宣告為解構函式,可能出現的結果如下:Derived物件的成分沒有被銷燬,形成資源洩露、在除錯上會浪費很長時間。

#include <iostream>
using  namespace std;


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;
system("pause"); 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的成員變數尚未初始化。 【如果不】:執行的結果不會動態聯編,依然執行其所在層的虛擬函式。 3.綜合1,2的筆試題如下:

#include <iostream>
using  namespace std;




class CBase

{

public:

       CBase()
       { 
       	cout << "CBase ctor" << endl; foo(); 
       }    //呼叫本層的foo

        ~CBase() 
        {
         cout<< "CBase dtor" << endl; foo();
         }  //未加virtual,且呼叫本層的foo

private:

       virtual void 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;

 
	   system("pause");
       return 0;

}


 

在這裡插入圖片描述 顯然,1.CDerived的解構函式不會被呼叫,因為CBase的解構函式非虛擬函式。 2.在CBase的構造和解構函式中呼叫虛擬函式,僅會執行本層的定義,不會下調。還是遵照[Effective 原則09]:絕不在構造和析構過程中呼叫virtual函式為上策。