《Effective C++》說:
因爲C++明確指出,當derived class對象經由一個base class指針被刪除,而base class帶着一個non-virtual析構函數,其結果未有定義---實際執行時通常發生的是對象的derived成分沒被銷燬。如:baseclass *ptb = derivedclassFuc() //子類的一個函數; 其中baseclass中有個non-virtual析構函數。解決這一問題的方法就是把基類中的析構函數聲明爲virtual,通常還有其他virtual函數。任何class只要還有virtual函數都幾乎確定應該也有一個virtual析構函數。
如果class不含virtual函數,通常表示它並不意圖被用做一個base class.當class不企圖被當作base class,令其析構函數爲virtual往往是個餿主意。許多人的心得是:只有當class內含至少一個virtual函數,才爲它聲明virtual析構函數。
有時候令Class帶一個pure virtual析構函數,可能頗爲便利,純虛函數導致抽象類,也就是不能被實體化的類,你不能爲這種類型創建對象。所以爲你希望它成爲抽象的那個類聲明一個pure virtual析構函數,這樣你就不需要擔心析構函數的問題了,不過你必須爲這個pure virtual析構函數提供一份定義:如,AWOV::~AWOV ( ) { …….},析構函數的運作方式是,最深層派生的那個class其析構函數最先被調用,然後是其每一個base class的析構函數被調用。編譯器會在AWOV的derived classes的析構函數中創建一個對~AWOV的調用動作,所以你必須爲這個函數提供一份定義,否則,連接器會出問題。
=======================================
把析構函數定義成虛函數,可以有效的防止在之後繼承的時候,子類指針的內存泄露.
比如,
class A
class B:public A
A *ptr=new B
在這種情況下調用析構函數的時候,就會發生刪除了A的空間而沒有刪除B的空間,
所以將基類定義爲抽象類,可以讓其子類動態的去調用它自身的函數。
========================================
class Base
{
public:
Base(){}
virtual void VirFun()
{
}
~Base(){} //實際上這裏必須加virtual
private:
//Some attribute
};
class Sub : public Base
{
public:
Sub(){}
virtual void VirFun()
{
}
~Sub(){}
private:
//Some attribute
};
上面的例子是個簡單的多態。
在我們應用的時候,我們經常會這麼幹:
Base * pBase = new Sub();//new了一個Sub對象,它實際上包含了Base 的子對象。
pBase->VirFun(); //很好,它會根據實際的類型調用Sub 的 VirFun()
delete pBase;//完蛋了,它調用了Base的析構。Sub的析構?這可不關我事,我可是Base類型的。
於是內存泄漏了。。。
給Base的析構函數加上virtual之後呢?
delete pBase; //很好,它繼續多態調用實際類型對象的析構。
大家知道,子類的析構函數會自動調用父類的析構。
於是,啊哈,終於安全了。
至此,大家應該對題目的意思有了大概的瞭解了。
有沒有發現,virtual 析構函數挺有意思的,它的子類析構函數名字和父類不同,一樣會重寫父類的析構函數。
爲什麼題目要強調“多態 基類”呢?
class Base
{
public:
Base(){}
void Fun()
{
}
virtual ~Base(){}
private:
//Some attribute
};
class Sub : public Base
{
public:
Sub(){}
void Fun()
{
}
~Sub(){}
private:
//Some attribute
};
這樣的繼承中,有沒有必要給父類析構函數加virtual呢?
從語法意義上來說,是應該的,因爲你像下面這樣寫的時候,語法是沒有錯誤的:
Base * pBase = new Sub();//new了一個Sub對象,它實際上包含了Base 的子對象。
delete pBase; //很好啊,程序多安全啊。有什麼問題嗎?
在這樣寫的時候,你應該譴責自己。
pBase的所有行爲都是Base中定義的,爲什麼要給它一個Sub的身體呢?
(爲什麼網上很多男士都愛用美女作頭像呢。。。)
所以,當你設計的類系不具備多態性的時候,請不要:
1.用父類的指針指向子類;
2.給父類的析構函數加上virtual(畢竟虛函數表也是不小的開銷不是?)