C 中虛函式

2021-06-28 22:59:27 字數 2289 閱讀 2705

虛函式聯絡到多型,多型聯絡到繼承。所以本文中都是在繼承層次上做文章。沒了繼承,什麼都沒得談。

下面是對c++的虛函式這玩意兒的理解。

一, 什麼是虛函式(如果不知道虛函式為何物,但有急切的想知道,那你就應該從這裡開始)

簡單地說,那些被virtual關鍵字修飾的成員函式,就是虛函式。虛函式的作用,用專業術語來解釋就是實現多型性(polymorphism),多型性是將介面與實現進行分離;用形象的語言來解釋就是實現以共同的方法,但因個體差異而採用不同的策略。下面來看一段簡單的**

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; 這個我不太了解,算了,不解釋這個了

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;和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)的長度

三, 以一段**開始

#includeusing namespace std;

class a

你能估算出輸出結果嗎?如果你估算出的結果是a::fun和a::fun2,呵呵,恭喜恭喜,你中圈套了。其實真正的結果是b::fun和b::fun2,如果你想不通就接著往下看。給個提示,&a::fun和&a::fun2是真正獲得了虛函式的位址嗎?

首先我們回到第二部分,通過段實作**,得到乙個「通用」的獲得虛函式位址的方法

#includeusing namespace std;

//將上面「虛函式示例**2」新增在這裡

void callvirtualfun(void* pthis,int index=0)

int main()

現在我們擁有乙個「通用」的callvirtualfun方法。

這個通用方法和第三部分開始處的**有何聯絡呢?聯絡很大。由於a::fun()和a::fun2()是虛函式,所以&a::fun和&a::fun2獲得的不是函式的位址,而是一段間接獲得虛函式位址的一段**的位址,我們形象地把這段**看作那段callvirtualfun。編譯器在編譯時,會提供類似於callvirtualfun這樣的**,當你呼叫虛函式時,其實就是先呼叫的那段類似callvirtualfun的**,通過這段**,獲得虛函式位址後,最後呼叫虛函式,這樣就真正保證了多型性。同時大家都說虛函式的效率低,其原因就是,在呼叫虛函式之前,還呼叫了獲得虛函式位址的**。

最後的說明:本文的**可以用vc6和dev-c++4.9.8.0通過編譯,且執行無問題。其他的編譯器小弟不敢保證。其中,裡面的模擬方法只能看成模型,因為不同的編譯器的低層實現是不同的。例如this指標,dev-c++的gcc就是通過壓棧,當作引數傳遞,而vc的編譯器則通過取出位址儲存在ecx中。所以這些模擬方法不能當作具體實現

C 中的虛函式 純虛函式

c 最重要的特性就是多型,而多型,就主要通過虛函式實現的。具體的實現過程是 基類中的函式定義為虛函式,派生類發生覆蓋 即函式名稱 引數列表 返回值型別完全相同 的情況下,派生類中的函式也會自動變成虛函式,不論加不加virtual關鍵字。此時,基類與子類物件中都會存在一張虛函式表 因為含有虛函式 具體...

C 中函式與虛函式

基類的析構函式需要宣告為虛函式 當 derived class 物件經由乙個base class指標被刪除,而該base class帶著乙個non virtual析構函式,實際執行時通常發生的是物件的derived成分沒有被銷毀。區域性銷毀 class base base protected int...

C 中虛函式與函式

析構函式為什麼要宣告為虛 函式?基類的析構函式需要宣告為虛函式 當派生類物件經由乙個基類指標被刪除,而該基類帶著乙個non virtual析構函式,實際執行時通常發生的是物件的派生類成員沒有被銷毀。這也就是區域性銷毀,會發生記憶體洩漏,所以我們通常將基類的析構函式需要宣告為虛函式。class bas...