c++多態最清晰的解釋

先佔個位置,稍微寫點,這兩天忙完了寫代碼版:

關於虛函數與多態,一般是用微軟的解釋:基類指針或引用指向派生類對象時,如果調用虛函數,則調用派生類虛函數。
我看倒不如這樣來解釋:另寫一個函數,如  func (A&a) {}  其中一個參數是基類指針或引用,那麼我在主函數中調用它時,傳入參數是基類對象則是a是基類引用,傳入派生類對象則a是派生類引用,那麼就可以用a調用基類或者派生類虛函數,即在基類指針或引用虛函數的支持下可以實現動態綁定

借用c++primer上的一段話:當我們使用基類的引用或指針調用基類中定義的一個函數時,我們並不知道該函數真正作用的對象是什麼類型,因爲它可能是一個基類的對象也可能是一個派生類對象。如果該函數是虛函數,則直到運行時纔會決定到底執行哪個版本,判斷的依據是引用或指針所綁定的對象的真實類型。即對非虛函數的調用在編譯時進行綁定。

舉例如下:

#include <iostream>
using namespace std;

class father
{
public:
	virtual void fA()    
	{
		cout << "father fA" << endl;
	}
	virtual void fB()
	{
		cout << "father fB" << endl;
	}
};
class son : public father
{
public:
	virtual void fA()
	{
		cout << "son fA" << endl;
	}
	virtual void fC()
	{
		cout << "son fC" << endl;
	}
};

void func(father *obj)
{
	obj->fA();
}
int main()
{
	father *f = new father;
	son *s = new son;
	func(f);
	func(s);

	return 0;
}

輸出如下:

上面就是多態的一個使用樣例。

那麼c++語言具體如何實現多態的呢?需要知道兩個東西!

第一:虛函數表,什麼意思?

就是說一個類一旦擁有虛函數,則會擁有一張虛函數表,這個表獨立於對象,是屬於類本身的,且這張表會被派生類繼承,其中子類重寫的虛函數則會使用子類的虛函數地址替代,比如上面的連個類,它們的虛函數表大致如下:

父類:

father::fA()
father::fB()

子類:

father::fA()
son::fB()
son::fC()

格式:首先是父類虛函數,然後是子類虛函數,父類被重寫部分會被子類的虛函數替代

第二:位於對象頭部的虛表地址,什麼意思?

當一個類被實例化的時候,這個實例化對象的首地址其實是一個指向該類虛表的指針。

因此當程序運行到這裏時,會通過頭部的地址查找虛表中的函數來進行調用。

那麼剛剛說的兩點:虛表?+ 在頭部?真的是這樣嗎,不如來驗證一下,畢竟實踐出真知:

計劃:既然虛函數是一個函數,那麼假設剛剛的兩點是真的,就一定能按照上述的方法使用函數指針來調用到虛函數

代碼如下:

int main()
{
	father *f = new father;
	void( *p )( );
	//首先取到對象的地址,也就是: f
	//然後地址的第一個位置存儲的便是虛函數表的地址:*(int*)f
	//然後通過虛表就可以訪問函數,比如第一個函數的地址就是:*(int*)*(int*)f
	p = ( void(*)( ) )*( ( int * )*( (int*) f ) ); //第一個函數
	p();
	p = ( void(*)( ) )*( ( int * )*( (int*) f ) + 1 ); //第二個函數
	p();
	return 0;
}

結果如下:

可以發現,都順利訪問到了,因此上面說的那兩點是對的!

這裏解釋一下,爲什麼取地址的時候需要轉換爲int*,因爲取地址的時候面對的是一個數值串,取地址的時候轉換成int*就是告訴計算機要從這個地方取4個字節的數值,將它當作地址,況且不這麼做怎麼知道+1的時候移動多少呢。

那麼動態綁定實現原理這裏就解釋完了。

最後,再囉嗦一句,可能有人被基類指針這個地方迷糊住了,說爲啥必須要用基類指針,而不能派生類指針呢,其實大可不必迷惑,因爲這是語法上不合邏輯的,因爲派生類本來就比基類的東西多,萬一訪問到基類不存在的呢,這不是出了岔子?其實簡而言之,如果用派生類訪問基類對象,編譯這一關都過不了嘻嘻。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章