您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關(guān)帶你了解C++ 中的虛函數(shù),小編覺得挺實用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
先從對象大小開始
假設(shè)我們有如下代碼,假設(shè) int 占 4 字節(jié),指針占 4 字節(jié)。
#include "stdafx.h" #include "stdlib.h" #include "stddef.h" class CBase { public: virtual void VFun1() { printf(__FUNCTION__ "\n"); } virtual void VFun2() { printf(__FUNCTION__ "\n"); } virtual ~CBase() { printf(__FUNCTION__ "\n"); } int data; }; class CDerived : public CBase { public: virtual void VFunNew() { printf(__FUNCTION__ "\n"); } virtual void VFun1() override { printf(__FUNCTION__ "\n"); } virtual ~CDerived() override { printf(__FUNCTION__ "\n"); } }; int _tmain(int argc, _TCHAR* argv[]) { printf("sizeof CBase is: %d, offset of data is %d\n", sizeof(CBase), offsetof(CBase, data)); system("pause"); CBase* pBase = new CDerived(); pBase->VFun1(); pBase->VFun2(); system("pause"); return 0; }
輸出結(jié)果如下圖:
有沒有覺得意外?從類定義可知,data 占 4 字節(jié),那另外的 4 字節(jié)是哪里來的呢?data 的偏移值不應(yīng)該是 0 嗎?為什么是 4 呢?
內(nèi)存布局
如果一個類有虛函數(shù),編譯器會自動為這個類型的對象在頭部增加一個虛表指針(vftable),指向虛函數(shù)表。虛函數(shù)表中存放著一個個的虛函數(shù)。
CBase
和 CDerived
類對象的內(nèi)存布局如下:
注意:虛函數(shù)表中索引為 -1 的地方指向了跟動態(tài)類型轉(zhuǎn)換相關(guān)的信息。
虛表指針的初始化
vftable
是在類的構(gòu)造函數(shù)中初始化的??梢栽?code> IDA 中分別查看 CBase
類 和 CDerived
類的構(gòu)造函數(shù)的反匯編代碼。
CBase
構(gòu)造函數(shù)的反匯編代碼如下(關(guān)鍵部分已注釋):
由反匯編代碼可知, CBase
的構(gòu)造函數(shù)會把 CBase
對象開始的位置(存放虛表指針)設(shè)置為 CBase::vftable
。
CDerived
構(gòu)造函數(shù)的反匯編代碼如下(關(guān)鍵部分已注釋):
由反匯編代碼可知, CDerived
的構(gòu)造函數(shù)會先調(diào)用 CBase
的構(gòu)造函數(shù)進行基類部分的初始化,在 CBase
構(gòu)造函數(shù)的內(nèi)部把 CDerived
對象開始的位置設(shè)置為 CBase::vftable
,然后調(diào)用自身的初始化部分,會把 CDerived::vftable
的地址放到對象開始的位置,從而替換掉了 CBase 類的虛表指針。
虛函數(shù)表的內(nèi)容
了解完了虛表指針的初始化過程,再來看看 vftable
里面都有哪些內(nèi)容。
可以雙擊 ??_7CBase@@6B@
(或者直接按回車)跳轉(zhuǎn)到虛表所在的地方。如下圖:
說明:上側(cè)是 CBase
類的虛表內(nèi)容,下側(cè)是 CDerived
類的虛表內(nèi)容。
請注意圖片上側(cè)黃色高亮部分,也就是 vftable[-1] 的地方,是跟動態(tài)類型轉(zhuǎn)換相關(guān)的信息,后面有機會介紹。
虛函數(shù)調(diào)用
理解了類對象的內(nèi)存布局及虛函數(shù)表之后,再理解虛函數(shù)的調(diào)用過程就比較簡單了。
有些 C++ 基礎(chǔ)的小伙伴兒都知道本例中的輸出結(jié)果應(yīng)該如下圖所示:
直接看一下 pBase->VFun1()
和 pBase->VFun2()
對應(yīng)的反匯編代碼就應(yīng)該明白一切了。如下圖:
因為 pBase
指向的實際是 CDerived
類型的對象,所以虛表是 CDerived
類的。如下圖所示
經(jīng)過以上的分析,輸出結(jié)果合情合理。
說明
本文只是拿了一個最最簡單的例子做演示。像多重繼承,虛繼承等比較復(fù)雜的情況,感興趣的小伙伴可以自行研究。
雖然這個例子很簡單,但是背后的機理值得了解清楚,非常有用。比如,當庫中的接口與庫頭文件不匹配的時候,很可能莫名其妙的就崩潰了。這時可以通過查看指針對應(yīng)的虛表的內(nèi)容來查看庫中的虛函數(shù)都有哪些,跟頭文件對比后就可以比較準確的判斷是否是庫不匹配的問題。還可以根據(jù)虛表的內(nèi)容,猜測出基類指針指向的具體的子類對象的類型。
可以在 windbg
中使用 dps
命令快速打印,如下圖:
總結(jié)
注意:如果通過對象調(diào)用虛函數(shù),會是另外一種情況,因為不存在多態(tài),直接使用函數(shù)低級進行調(diào)用就可以了。感興趣的小伙伴兒可以自行實驗。
以上就是帶你了解C++ 中的虛函數(shù),小編相信有部分知識點可能是我們?nèi)粘9ぷ鲿姷交蛴玫降?。希望你能通過這篇文章學(xué)到更多知識。更多詳情敬請關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。