虛函式聯絡到多型,多型聯絡到繼承。關於多型,簡而言之就是用父型別別的指標指向其子類的例項,然後通過父類的指標呼叫實際子類的成員函式。這種技術可以讓父類的指標有「多種形態」,這是一種泛型技術。所謂泛型技術,說白了就是試圖使用不變的**來實現可變的演算法。比如:模板技術,rtti技術,虛函式技術,要麼是試圖做到在編譯時決議,要麼試圖做到執行時決議。
下面來看一段簡單的**
class a
執行一下看看結果,結果卻是兩個this is a。問題來了,p2明明指向的是class b的物件但卻是呼叫的class a的print()函式,這不是我們所期望的結果,那麼解決這個問題就需要用到虛函式。
class a
用vc或dev-c++編譯執行一下,看看結果是不是輸出3,如果不是,那麼太陽明天肯定是從西邊出來。現在一步一步開始分析。
void (*fun)(a*);這段定義了乙個函式指標名字叫做fun,而且有乙個a*型別的引數,這個函式指標待會兒用來儲存從vtbl裡取出的函式位址。
a* p=new b; new b是向記憶體(記憶體分5個區:全域性名字空間,自由儲存區,暫存器,**空間,棧)自由儲存區申請乙個記憶體單元的位址然後隱式地儲存在乙個指標中.然後把這個位址賦值給a型別的指標p。
long lvptraddr; 這個long型別的變數待會兒用來儲存vptr的值。
memcpy(&lvptraddr,p,4); 前面說了,他們的例項物件裡只有vptr指標,所以我們就放心大膽地把p所指的4bytes記憶體裡的東西複製到lvptraddr中,所以複製出來的4bytes內容就是vptr的值,即vtbl的位址。
現在有了vtbl的位址了,那麼我們現在就取出vtbl第乙個slot裡的內容。
memcpy(&fun,reinterpret_cast(lvptraddr),4);取出vtbl第乙個slot裡的內容,並存放在函式指標fun裡。需要注意的是lvptraddr裡面是vtbl的位址,但lvptraddr不是指標,所以我們要把它先轉變成指標型別。
fun(p);這裡就呼叫了剛才取出的函式位址裡的函式,也就是呼叫了b::fun()這個函式,也許你發現了為什麼會有引數p,其實類成員函式呼叫時,會有個this指標,這個p就是那個this指標,只是在一般的呼叫中編譯器自動幫你處理了而已,而在這裡則需要自己處理。
delete p; 釋放由p指向的自由空間。
system("pause"); 螢幕暫停。
如果呼叫b::fun2()怎麼辦?那就取出vtbl的第二個slot裡的值就行了。
memcpy(&fun,reinterpret_cast(lvptraddr+4),4); 為什麼是加4呢?因為乙個指標的長度是4bytes,所以加4。或者memcpy(&fun,reinterpret_cast(lvptraddr)+1,4); 這更符合陣列的用法,因為lvptraddr被轉成了long*型別,所以+1就是往後移sizeof(long)的長度。
再看下一段**
#include
using namespace std;
class a
你能估算出輸出結果嗎?如果你估算出的結果是a::fun和a::fun2,呵呵,恭喜恭喜,你中圈套了。其實真正的結果是b::fun和b::fun2,如果你想不通就接著往下看。給個提示,&a::fun和&a::fun2是真正獲得了虛函式的位址嗎?
首先我們回到第二部分,通過段實作**,得到乙個「通用」的獲得虛函式位址的方法
#include
using namespace std;
//將上面「虛函式示例**2」新增在這裡
void callvirtualfun(void* pthis,int index=0)
int main()
callvirtualfun方法
現在我們擁有乙個「通用」的callvirtualfun方法。這個通用方法和第三部分開始處的**有何聯絡呢?聯絡很大。由於a::fun()和a::fun2()是虛函式,所以&a::fun和&a::fun2獲得的不是函式的位址,而是一段間接獲得虛函式位址的一段**的位址,我們形象地把這段**看作那段callvirtualfun。編譯器在編譯時,會提供類似於callvirtualfun這樣的**,當你呼叫虛函式時,其實就是先呼叫的那段類似callvirtualfun的**,通過這段**,獲得虛函式位址後,最後呼叫虛函式,這樣就真正保證了多型性。
利用虛函式實現多型
關鍵字 virtual 針對基類的不同派生類,如果派生類重新定義虛函式,那麼派生類的虛函式將覆蓋基類對應虛函式的實現。如果強制派生類定義某個函式,則可以在基類中將這個虛函式宣告為純虛函式,也就是基類不實現這個虛函式,所有實現留給派生類。當類中含有純虛函式時,這個類就是抽象類。不能建立抽象類的物件,否...
多型實現及虛函式
多型是c 物件導向三大特性之一 多型分為兩類 1.靜態多型 函式過載和運算子過載屬於靜態多型 2.動態多型 派生類與虛函式實現執行時多型 區別 靜態多型的函式位址早繫結 編譯階段確定函式位址 動態多型的函式位址晚繫結 執行階段確定函式位址 class animal class cat public ...
多型實現之虛函式
多型的實現分為靜態多型和動態多型,靜態多型主要靠函式過載,動態多型主要靠虛函式 當類中宣告了虛函式之後,該類的記憶體映像會獲得乙個虛表指標,叫做 vfptr指向該類的虛表,下面的我測試使用的類圖 有的沒必要的東西沒有寫 這裡恰好還是乙個菱形繼承,但是就像我之前說的虛繼承和虛表關係不大,所以單繼承也是...