1. 程式人生 > >虛擬函式實現機制、建構函式、解構函式能否為虛擬函式,與純虛擬函式

虛擬函式實現機制、建構函式、解構函式能否為虛擬函式,與純虛擬函式

  •  虛擬函式

虛擬函式是C++中用於實現多型的機制。核心理念就是通過基類指標訪問派生類中定義的函式。虛擬函式允許子類重新定義成員函式繼承時不重新定義虛擬函式也是可以的。而子類重新定義父類的做法稱為覆蓋(override),或者稱為重寫。如果父類或者祖先類中函式func()為虛擬函式,則子類及後代類中,函式func()是否加virtual關鍵字,都將是虛擬函式。為了提高程式的可讀性,建議後代中虛擬函式都加上virtual關鍵字。

  •  虛擬函式實現機制

基類的指標執行到相應的程式碼處時,能確定指標指向的是哪一個物件,從而呼叫相應的虛擬函式指標vptr,找到相應虛擬函式表中的相應虛擬函式。

虛擬函式表:類的虛擬函式表是一塊連續的記憶體,每個記憶體單元中記錄一個JMP指令的地址。

  編譯器會為每個有虛擬函式的類建立一個虛擬函式表,該虛擬函式表將被該類的所有物件共享。類的每個虛擬函式佔據虛擬函式表中的一塊。如果類中有N個虛擬函式,那麼其虛擬函式表將有N*4位元組的大小。

  在有虛擬函式的類的例項中分配了指向這個表的指標的記憶體,所以,當用父類的指標來操作一個子類的時候,這張虛擬函式表就顯得尤為重要了,它就像一個地圖一樣,指明瞭實際所應該呼叫的函式。

  編譯器應該是保證虛擬函式表的指標存在於物件例項中最前面的位置(這是為了保證取到虛擬函式表的有最高的效能——如果有多層繼承或是

多重繼承的情況下)。 這意味著可以通過物件例項的地址得到這張虛擬函式表,然後就可以遍歷其中函式指標,並呼叫相應的函式。

  ->有虛擬函式或虛繼承的類例項化後的物件大小至少為4位元組(確切的說是一個指標的位元組數;說至少是因為還要加上其他非靜態資料成員,還要考慮對齊問題);沒有虛擬函式和虛繼承的類例項化後的物件大小至少為1位元組(沒有非靜態資料成員的情況下也要有1個位元組來記錄它的地址)。

  有純虛擬函式的類為抽象類,不能定義抽象類的物件,它的子類要麼實現它所有的純虛擬函式變為一個普通類,要麼還是一個抽象類。

特別的:

  (1)當存在類繼承並且解構函式中有必須要進行的操作時(如需要釋放某些資源,或執行特定的函式)

解構函式需要是虛擬函式,否則若使用父類指標指向子類物件,在delete時只會呼叫父類的解構函式,而不能呼叫子類的解構函式,從而造成記憶體洩露或達不到預期結果;

  (2)虛擬函式能不能是行內函數要取決於具體情況:虛擬函式是通過基類的物件呼叫的就可以內聯,如果虛擬函式是通過基類指標指向的子類物件,這個時候是要多型呼叫,不能在編譯期間確定內聯,所以是不能內聯的。其實inline都只是一個申請,最終由編譯器決定內聯還是不內聯。如下:b是基類物件,可以內聯;ptr是基類指標,但是指向的是子類物件,這個時候不能內聯。

#include <iostream>  
using namespace std;
class Base
{
public:
	inline virtual void who()
	{
		cout << "I am Base\n";
	}
	virtual ~Base() {}
};
class Derived : public Base
{
public:
	inline void who()  // 不寫inline時隱式內聯
	{
		cout << "I am Derived\n";
	}
};

int main()
{
	// 此處的虛擬函式 who(),是通過類(Base)的具體物件(b)來呼叫的,編譯期間就能確定了,所以它可以是內聯的,但最終是否內聯取決於編譯器。 
	Base b;
	b.who();

	// 此處的虛擬函式是通過指標呼叫的,呈現多型性,需要在執行時期間才能確定,所以不能為內聯。  
	Base *ptr = new Derived();
	ptr->who();

	// 因為Base有虛解構函式(virtual ~Base() {}),所以 delete 時,會先呼叫派生類(Derived)解構函式,再呼叫基類(Base)解構函式,防止記憶體洩漏。
	delete ptr;
	ptr = nullptr;

	system("pause");
	return 0;
} 

  (3)建構函式不能為虛擬函式:建構函式在進行呼叫時還不存在父類和子類的概念,父類只會呼叫父類的建構函式,子類呼叫子類的,因此不存在動態繫結的概念;但是建構函式中可以呼叫虛擬函式,不過並沒有動態效果,只會呼叫本類中的對應函式;

  (4)靜態成員函式不能為虛擬函式:靜態成員函式是以類為單位的函式,與具體物件無關,虛擬函式是與物件動態繫結的。