虛函式表存放位置

2021-06-10 12:12:39 字數 3039 閱讀 6199

引言:近日

csdn的"c/c++語言"版的乙個問題

引起了我的注意:"請問虛函式表放在**?"。我也曾經思考過這個問題,零零散散也有一定的收穫,這次正好趁這個機會把我對這一部分的理解整理一下。 首先值得宣告的是,本文的編譯環境是vs2002+winxp。c++標準並沒有對虛函式的實現作出任何的說明,甚至都沒有提到虛函式的實現需要用虛表來實現,只不過主流的c++編譯器的虛函式機制都是通過虛表來實現的,所以用虛表來實現虛函式就成了"不是標準的標準"。但是這並不代表所有編譯器在實現細節上的處理都是完全一致的,它們或多或少都存在一定的個體差異。所以,本文的結論不一定適用於其他的編譯情況。

虛函式/虛表的基礎知識

乙個類存在虛函式,那麼編譯器就會為這個類生成乙個虛表,在虛表裡存放的是這個類所有虛函式的位址。當生成類物件的時候,編譯器會自動的將類物件的前四個位元組設定為虛表的位址,而這四個位元組就可以看作是乙個指向虛表的指標。虛表裡依次存放的是虛函式的位址,每個虛函式的位址佔4個位元組。

編譯模組內部虛表存放的位置

const    segment

??_7cderived@@6b@                                     ; cderived::`vftable

'dd flat:

?foobar@cderived@@uaexxz

dd flat:

?callme@cderived@@uaexxz

; function compile flags: 

/odt 

/rtcsu /zi

const    ends

以上的彙編**給了我們這樣的資訊:

1> 虛表存放的位置應該實在模組的常量段中;

2> 這個類有兩個虛函式,它們分別是?foobar@cderived@@uaexxz和?callme@cderived@@uaexxz。

外部模組虛表存放的位置

當乙個模組匯出了乙個帶虛表的類,而另外乙個模組又使用了這個匯出類,這時候情況又是什麼樣的呢?這裡存在兩種很自然的處理方式:

1。維護乙份虛表。虛表放在定義匯出類的那個模組,任何使用這個匯出類的其他模組都要通過這個模組來使用匯出類。

2。維護多份虛表。這時候每乙個使用匯出類的模組都會有乙份虛表的拷貝。

vs2002是使用那一種情況呢?在假設存在多份虛表的前提下,我們可以使用這樣的策略來判斷vs2002使用那種方式:

1。在類定義模組中建立乙個類物件,並在另外乙個模組中使用這個類物件。在類定義模組中建立類物件保證編譯器用類定義模組中的虛表來初始化類物件。

2。在模組(非類定義模組)中建立並類物件並使用它。這樣就保證編譯器會用模組中的虛表來初始化類物件。

3。分別獲取兩種情況下兩個類物件的虛表指標。如果它們的值相等,就說明只存在乙份虛表;如果它們的值不等,就說明存在多份虛表。

4。如果兩個虛表指標的值相等,則虛表來自於兩個模組中的乙個模組,判斷這個虛表來自於那個模組。

cdllindepth

* pobjinanotherdll = createobject();

intvtableadress = *reinterpret_cast<

int*>(pobjinanotherdll);

intvfuncaddress = *reinterpret_cast<

intnew

cdllindepth;

intvtableadress2 = *reinterpret_cast<

intint

vfuncaddress2 = *reinterpret_cast<

int

對這段**做如下的解釋:

1。createobject()是dll匯出了乙個全域性函式。這個全域性函式實現的功能就是生成乙個類物件並將類物件的位址傳出。這樣做的目的就是為了在類定義模組中生成乙個類物件。

2。獲得虛表指標和虛函式的**可以這樣分析:由於虛表指標存放在類物件的前4個位元組中,我們首先需要將類物件的首位址轉化成int型指標,並通過這個int型指標獲得前4個位元組的內容,這個內容就是虛表的位址。接著我們將這個虛表的位址再轉化成int型指標,並通過這個int型指標獲得虛表的前4個位元組的內容,這個內容就是虛表的第一項的值,也就是乙個虛函式的位址。

通過除錯,我們得出這樣的結果:

vtableadress  

=0x1001401c

vfuncaddress  

=0x1001103c

vtableadress2 

=0x1001401c

vfuncaddress2 

=0x1001103c

00400000

-00417000

dllindepth.dll         

10000000

-10019000 後記

雖然本文的主要內容是討論虛表的位置,實際上本文涉及到dll匯出類的內容。在論壇上也經常看到一些網友對dll匯出類的內容感到迷惑。相對於簡單的函式和資料,類的構成將顯得比較複雜,類宣告中可以包含任意型別的資料,成員函式,虛函式,靜態函式,我們就不禁迷惑這些東西是以什麼樣的方式匯出並讓其他的模組使用的?對於這個問題,我不禁想到了乙個很有名的縮寫"

kiss(keep it ******, stupid)

"。這是乙個很有用的思維方式,我們就不妨嘗試使用這種思維方法從簡單的出發點開始思考。對於dll來說,作為乙個模組,使用者感興趣的無非是**和資料:

在對c++類的結構(或者說模型)進行深入分析的基礎上,我們知道,對於c++類,它既有**,也有資料:

由此可見,從本質上來說,dll匯出類的情況就是匯出函式和資料,並沒有什麼神秘的。如果我們再加上類的特殊性的分析,問題的答案就清晰了:

對於成員函式,虛函式,靜態函式和靜態資料,他們都處於類的作用域內,所以他們匯出的函式符號中應該包含類的資訊。

對於成員函式和虛函式,他們的第乙個引數應該是指向類物件的指標,並且他們以"__thiscall"的呼叫習慣(calling convention)呼叫。

對於類的靜態函式和靜態資料,dll按照全域性函式和全域性資料的處理方式一樣處理他們。

虛表是以常量的形式匯出的。

^_^,dll匯出類的情況盡是如此的簡單,沒有想到吧,不過"情況就是這樣的"。

虛函式表存放位址

1.虛函式表是全域性共享的元素 即全域性僅有乙個.2.虛函式表類似乙個陣列 類物件中儲存 vptr指標,指向虛函式表 即虛函式表不是函式 不是程式 不肯能儲存在 段.3.虛函式表儲存虛函式的位址 即虛函式表的元素是指向類成員函式的指標 而類中虛函式的個數在編譯時期可以確定 即虛函式表的大小可以確定 ...

虛函式表的位置

引言 近日 csdn的 c c 語言 版的乙個問題 引 起了我的注意 請問虛函式表放在 我也曾經思考過這個問題,零零散散也有一定的收穫,這次正好趁這個機會把我對這一部分的理解整理一下。首先值得宣告的是,本文的編譯環境是vs2002 winxp。c 標準並沒有對虛函式的實現作出任何的說明,甚至都沒有提...

C MFC VC 虛函式表指標的位置

編譯器會把一種叫虛指標 vptr 的隱藏資料插入到至少擁有乙個虛函式的類中。vptr 是一種指向虛函式位址列表的指標。在不同編譯器中,vptr 所在位置是不同的。一些編譯器 例如 visual c 和 c builder 把 vptr 放置在類的開頭部分,在所有使用者宣告的資料成員的前面。而另一些編...