C 虛函式簡介

2021-10-09 10:36:23 字數 2301 閱讀 2959

假設我們有如下**,假設int4位元組,指標佔4位元組。

#include "stdafx.h"

#include "stdlib.h"

#include "stddef.h"

class cbase

virtual void vfun2()

virtual ~cbase()

int data;

};class cderived : public cbase

virtual void vfun1() override

virtual ~cderived() override

};int _tmain(int argc, _tchar* ar**)

輸出結果如下圖:

有沒有覺得意外?從類定義可知,data4位元組,那另外的4位元組是**來的呢?data的偏移值不應該是0嗎?為什麼是4呢?

如果乙個類有虛函式,編譯器會自動為這個型別的物件在頭部增加乙個虛表指標(vftable),指向虛函式表。虛函式表中存放著乙個個的虛函式。

cbasecderived類物件的記憶體布局如下:

vftable是在類的建構函式中初始化的。可以在ida中分別檢視cbase類 和cderived類的建構函式的反彙編**。

cbase建構函式的反彙編**如下(關鍵部分已注釋):

![view-cbase-constructor-in-ida](

由反彙編**可知,cbase的建構函式會把cbase物件開始的位置(存放虛表指標)設定為cbase::vftable

cderived建構函式的反彙編**如下(關鍵部分已注釋):

![view-cderived-constructor-in-ida](

由反彙編**可知,cderived的建構函式會先呼叫cbase的建構函式進行基類部分的初始化,在cbase建構函式的內部把cderived物件開始的位置設定為cbase::vftable,然後呼叫自身的初始化部分,會把cderived::vftable的位址放到物件開始的位置,從而替換掉了cbase類的虛表指標。

了解完了虛表指標的初始化過程,再來看看vftable裡面都有哪些內容。

可以雙擊??_7cbase@@6b@(或者直接按回車)跳轉到虛表所在的地方。如下圖:

說明:上側是cbase類的虛表內容,下側是cderived類的虛表內容。

理解了類物件的記憶體布局及虛函式表之後,再理解虛函式的呼叫過程就比較簡單了。

有些c++基礎的小夥伴兒都知道本例中的輸出結果應該如下圖所示:

直接看一下pbase->vfun1()pbase->vfun2()對應的反彙編**就應該明白一切了。如下圖:

因為pbase指向的實際是cderived型別的物件,所以虛表是cderived類的。如下圖所示:

經過以上的分析,輸出結果合情合理。

本文只是拿了乙個最最簡單的例子做演示。像多重繼承,虛繼承等比較複雜的情況,感興趣的小夥伴可以自行研究。

雖然這個例子很簡單,但是背後的機理值得了解清楚,非常有用。比如,當庫中的介面與庫標頭檔案不匹配的時候,很可能莫名其妙的就崩潰了。這時可以通過檢視指標對應的虛表的內容來檢視庫中的虛函式都有哪些,跟頭檔案對比後就可以比較準確的判斷是否是庫不匹配的問題。還可以根據虛表的內容,猜測出基類指標指向的具體的子類物件的型別。

可以在windbg中使用dps命令快速列印,如下圖:

注意:如果通過物件呼叫虛函式,會是另外一種情況,因為不存在多型,直接使用函式低階進行呼叫就可以了。感興趣的小夥伴兒可以自行實驗。

《深度探索 c++ 物件模型》

虛繼承是什麼意思 C 虛函式簡介

假設我們有如下 假設int佔4位元組,指標佔4位元組。include stdafx.h include stdlib.h include stddef.h class cbase virtual void vfun2 virtual cbase int data class cderived pub...

C 虛函式 純虛函式

1 基本概念 虛函式是在基類中使用關鍵字virtual宣告的函式。在派生類中重新定義基類中定義的虛函式時,會告訴編譯器不要靜態鏈結到該函式。我們想要的是在程式中任意點可以根據所呼叫的物件型別來選擇呼叫的函式,這種操作被稱為動態鏈結,或後期繫結。您可能想要在基類中定義虛函式,以便在派生類中重新定義該函...

C 虛函式 純虛函式

1.析構函式是否應為虛函式問題?2.成員函式的虛函式問題?3.析構函式是否可以為純虛函式問題?說明 僅在使用父類指標指向子類物件時有區別 當析構函式非虛函式時,使用父類指標指向子類物件,在析構時將不會呼叫子類析構函式 當析構函式是虛函式時,使用分類指標指向子類物件,在析構時會呼叫子類析構函式,且呼叫...