1. 程式人生 > >基類解構函式與虛擬函式

基類解構函式與虛擬函式

    基類解構函式為什麼要宣告為虛擬函式?

    這個問題是C++理解虛擬函式和動態繫結的一個基礎,也是面試常考的一個點。雖然幾乎每本書中都會提及這個點,但是很少有把其具體闡明。本文通過具體例項+理論闡述儘量清晰解釋這個問題。

    首先看一下effective c++中是怎麼描述的。

   ----摘自《Effective C++》

    C++中明白指出,當derived class物件經由一個base class指標被刪除,而該base class帶著一個non-virtual 解構函式,其結果未有定義——實際執行時通常發生的是物件的derived成分沒有銷燬,然而其base class成分通常會被銷燬,於是造成一個詭異的“區域性銷燬”物件。

    要明白基類解構函式為什麼要宣告為虛擬函式,首先要理解幾個點。

  1. 動態繫結。繫結的是動態型別,就是對應的函式或者屬性依賴於物件的動態型別,是在執行階段確定的,不是編譯階段(靜態型別)。例項就是基類指標指向派生類,指向的是派生類中基類部分
  2. 在繼承體系中,建構函式和解構函式的執行順序。對於建構函式而言,是“從內而外”,具體指的是基類的建構函式先呼叫,然後才呼叫派生類的建構函式;對於解構函式而言,則是剛剛反過來,先呼叫派生類的解構函式,然後再呼叫基類的解構函式。
  3. 每個解構函式只能管理自己類中的元素。

    直接上程式碼吧。

#include <iostream>
using namespace std;

class Base
{
public:
	Base()
	{
		cout << "Base constructor" << endl;
	}
	virtual ~Base()
	{
		cout << "Base destructor" << endl;
	}

private:

};

class Derived:public Base
{
public:
	Derived():P(new int)
	{
		cout << "Derived constructor" << endl; 
	}

	~Derived()
	{
		delete p;
		cout << "Derived destructor" << endl; 
	}

private:
	int *p;
};

int main()
{
	Base *base =new Derived();
	delete base;
	system("pause");
	return 0;
}

    正常的基類中解構函式定義了virtual ,就會正常輸出。

    當把基類的解構函式virtual去掉,就會只調用基類析構,而不呼叫派生類的解構函式。這樣就會導致一些問題,因為在派生類構造的時候動態分配了一些記憶體,當派生類解構函式沒有呼叫時候,就會出現記憶體洩漏,同時派生類的成員也不會被銷燬。

     綜上,不一定所有的類都要把解構函式定義為虛擬函式,因為一旦定義為虛擬函式,其記憶體中就會增加4位元組的虛指標,但是為了對記憶體洩漏做到“零容忍”,建議只要這個類可能作為基類,都將其解構函式宣告為virtual。總體來說,基類虛擬函式宣告為virtual是為了在動態繫結時防止記憶體洩漏。