1. 程式人生 > 其它 >基類virtual解構函式的使用

基類virtual解構函式的使用

技術標籤:cpp指標多型c++抽象類

先來看一段程式(來自趨勢科技筆試):

#include <iostream>
using namespace std;
 
class Base_J 
{
public:
	Base_J()
	{
		cout << "Base Created" << endl;
	}
	~Base_J()
	{
		cout << "Base Destroyed" << endl;
	}
};
 
class Derived_J : public Base_J
{
public: Derived_J() { cout << "Derived Created" << endl; } ~Derived_J() { cout << "Derived Destroyed" << endl; } }; int main() { Base_J *pB = new Derived_J(); delete pB; pB = NULL; system("pause"); return 0; }

下面是它的執行結果:
在這裡插入圖片描述
上述結果比較詭異,按理說每個建立的類中的內容都應該在不用時被銷燬,但是上述只是銷燬了子類物件中屬於父類的部分,這是什麼原因呢?

先看一下幾個結論:

1,如果以一個基礎類指標指向一個衍生類物件(派生類物件),那麼經由該指標只能訪問基礎類定義的函式(“實函式”暫且這麼叫)( 靜態聯編

2,如果以一個衍生類指標指向一個基礎類物件,必須先做強制轉型動作(explicit cast),這種做法很危險,也不符合生活習慣,在程式設計上也會給程式設計師帶來困擾。(一般不會這麼去定義)

3,如果基礎類和衍生類定義了相同名稱的成員函式,那麼通過物件指標呼叫成員函式時,到底呼叫那個函式 要根據指標的原型來確定,而不是根據指標實際指向的物件型別確定。

虛擬函式就是為了對“如果你以一個基礎類指標指向一個衍生類物件,那麼通過該指標,你只能訪問基礎類定義的成員函式”這條規則反其道而行之的設計。

如果你預期衍生類由可能重新定義一個成員函式,那麼你就把它定義成虛擬函式( virtual )。
polymorphism就是讓處理基礎類別物件的程式程式碼能夠通透的繼續適當地處理衍生類物件。

純虛擬函式:
virtual void myfunc ( ) =0;
純虛擬函式不許定義其具體動作,它的存在只是為了在衍生類鐘被重新定義。 只要是擁有純虛擬函式的類,就是抽象類,它們是不能夠被例項化的( 只能被繼承)。 如果一個繼承類沒有改寫父類中的純虛擬函式,那麼他也是抽象類,也不能被例項化。
抽象類不能被例項化,不過我們可以擁有指向抽象類的指標,以便於操縱各個衍生類。
虛擬函式衍生下去仍然是虛擬函式,而且還可以省略掉關鍵字“virtual”。
引自http://blog.csdn.net/taoyingzhushui/article/details/8100434。

由上述可見,程式錯誤出在:解構函式本應該設定成虛擬函式。否則由上述第1條可知,在父類指標指向子類物件做物件銷燬時,由於解構函式不是虛擬函式,則delete時,父類指標只能呼叫父類自己的解構函式,這就造成了上述物件部分銷燬的錯誤狀況

《Effective C++》條款 07 p40:為多型基類宣告virtual解構函式

《C++ Primer 5》 p527:在C++語言中,當我們使用基類的引用(或指標)呼叫一個虛擬函式時將發生動態繫結

另外,Effective C++中有關於動態多型(執行時的多型:虛擬函式)和靜態多型(編譯時的多型:模板、過載)的說明。

現在我們將基類的析構改成虛擬函式試試:

class Base_J 
{
public:
	Base_J()
	{
		cout << "Base Created" << endl;
	}
	virtual ~Base_J()
	{
		cout << "Base Destroyed" << endl;
	}
};

結果便按照我們設想的執行了:
在這裡插入圖片描述
派生類建構函式和解構函式的執行順序

當建立物件時,編譯系統會自動呼叫建構函式。當撤銷物件時,編譯系統會自動呼叫解構函式。當建立派生類的物件時,首先執行基類的建構函式,然後執行派生類的建構函式。當撤銷物件時,則先執行派生類的解構函式,然後再執行基類的解構函式

《Effective C++》條款 07 p44:

帶多型性質的base classes應該宣告一個virtual解構函式。如果class帶有任何virtual函式,它就應該擁有一個virtual解構函式。 Classes的設計目的如果不是作為base classes 使用,或不是為了具備多型性,就不該宣告virtual解構函式。

父類指標指向子類物件,而子類物件卻經由父類指標被刪除,當父類有個non-virtual解構函式是,就會引起災難。

C++明確指出,當子類物件經由一個父類指標被刪除,而該父類帶有一個non-virtual解構函式,其結果未定義–實際執行時通常發生的是物件的derived成分沒有被銷燬。子類的解構函式也未能被執行。然而其base class成分通常會被銷燬,於是造成了詭異的“區域性銷燬”物件。這可能造成資源洩露,敗壞資料結構等