假設我們有如下**,假設int
佔4
位元組,指標佔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**)
輸出結果如下圖:
有沒有覺得意外?從類定義可知,data
佔4
位元組,那另外的4
位元組是**來的呢?data
的偏移值不應該是0
嗎?為什麼是4
呢?
如果乙個類有虛函式,編譯器會自動為這個型別的物件在頭部增加乙個虛表指標(vftable
),指向虛函式表。虛函式表中存放著乙個個的虛函式。
cbase
和cderived
類物件的記憶體布局如下:
vftable
是在類的建構函式中初始化的。可以在ida
中分別檢視cbase
類 和cderived
類的建構函式的反彙編**。
cbase
建構函式的反彙編**如下(關鍵部分已注釋):
由反彙編**可知,cbase
的建構函式會把cbase
物件開始的位置(存放虛表指標)設定為cbase::vftable
。
cderived
建構函式的反彙編**如下(關鍵部分已注釋):
由反彙編**可知,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 函式是什麼意思?
1.析構函式 destructor 與建構函式相反,當物件脫離其作用域時 例如物件所在的函式已呼叫完畢 系統自動執行析構函式。析構函式往往用來做 清理善後 的工作 例如在建立物件時用new開闢了一片記憶體空間,應在退出前在析構函式中用delete釋放 2.以c 語言為例 析構函式名也應與類名相同,只...
虛函式 虛繼承 C
關於虛表,我們就要用到乙個關鍵字 virtual,可以修飾函式,也可以修飾類。類的成員函式被virtual修飾之後,就成為了虛函式 修飾類,主要是虛繼承。在此之前,我們首先要了解乙個概念 物件模型,也就是說,乙個基類形成之後,裡面的成員是怎麼存放的,當派生類繼承基類之後,派生類的成員是怎麼存放的。我...
外虛內實是什麼意思 中醫講的虛與實是什麼意思
中醫常會說 虛 實 是大眾所熟悉的中醫用語,日常中 體虛 腎虛 等詞常被高頻使用,中醫角度說的虛實是何意思呢?虛實辯證乃是中醫辯證理論的重要組成部分。黃帝內經 素問 通評虛實論 中記載,精氣奪則虛,邪氣盛則實 黃帝內經 素問 調經論 又雲 百病之生,皆有虛實 虛則主要指正氣虛弱而邪氣不盛。虛則多與精...