虛函式表的實現細節

2021-06-18 15:11:09 字數 1781 閱讀 7670

1.虛函式

·虛表是怎麼實現的?

虛表存放在**?

·虛表中的資料是在什麼時候確定的?

·物件中的虛表指標又在什麼時候賦值的?

我們很難通過

c++語言本身來找到答案。

c++標準給編譯器實現者定義了語法規範,

但是被並沒有定義如何實現這些語法規範,不同的編譯器實現者可能有不同的實現方法,可以肯定的是他們的編譯器必須符合這些語法規範。

組合語言作為最接近機器語言的計算機語言,

可以為我們揭示一些隱藏在編譯器內部的細節。

接下來本來就試圖通過對

c++原始碼進行反彙編的方式來解答這些疑惑。

二、分析

這裡我選用 winxp 和 vs2008 作為我們這次分析的平台。我們建立乙個最簡單的 win32 控制台程式,並定義兩個簡單的類:

接下來我們可以直接編譯這些 c++原始碼就可以得到相應的彙編**。 通過分析這些彙編**我們就找到許多有用的資訊。我們可以找到這樣的彙編**:

以上的彙編**定義了兩個資料段, 而這兩個資料段中的內容恰好就是類的虛表。 至此虛表的"廬山真面目"完全展示在我們的面前。 根據這些資訊,我們可以推理出很多有用的結論:

·擁有虛函式的類會有乙個虛表,

而且這個虛表存放在類定義模組的資料段中。

模組的資料段通常存放定義在該模組的全域性資料和靜態資料,

這樣我們可以把虛表看作是模組的全域性資料或者靜態資料

·類的虛表會被這個類的所有物件所共享。

類的物件可以有很多,但是他們的虛表指標都指向同乙個虛表,從這個意義上說,我們可以把虛表簡單理解為類的靜態資料成員。

值得注意的是,雖然虛表是共享的,但是虛表指標並不是,類的每乙個物件有乙個屬於它自己的虛表指標。

·虛表中存放的是虛函式的位址。

另外乙個大的疑惑就是物件的虛表指標是在什麼時候被賦值的? 我們都知道,類的物件是通過建構函式來完成初始化,但是我們從來沒有在建構函式中初始化虛表指標, 那麼編譯器在幕後又做了哪些事情呢? 我們依然還是通過反彙編來找到答案。 在這個控制台程式的 main 函式中我們構建乙個類物件:

類的非靜態成員函式呼叫時,編譯器會傳入乙個"隱藏"的引數。 這個引數就是通常我們說的"this"指標,它的值就是物件的位址。 在上面的**中,暫存器 ecx 儲存的就是這個"

this" 指 針 , 同 時 它 的 值 又 賦 給 了 寄 存 器 eax。"??_7cd-szbase@@6b@"就是上面提到的虛表,同時它也代表了虛表的位址。

接下來,虛表的位址被賦給了由暫存器 eax 指定的記憶體中。由此可見,虛表的位址被存放在物件的起始位置,即物件的第乙個資料成員就是它的虛表指標。 同時我們還可以注意到,虛表指標的初始化確實發生在建構函式的呼叫過程中, 但是在執行建構函式體之前,即進入到建構函式的""之前。 為了更好的理解這一問題, 我們可以把建構函式的呼叫過程細分為兩個階段,即:

1.進入到建構函式體之間。

在這個階段如果存在虛函式的話,虛表指標被初始化。

如果存在建構函式的初始化列表的話,初始化列表也會被執行。

2.進入到建構函式體內。

這一階段是我們通常意義上說的建構函式

簡單的搞個基類base;public:int a;static b;}

定義乙個物件 b b;除錯狀態下就可以看到b包含了什麼

類中只有虛表指標和普通成員(包括

const

成員)而普通函式,靜態成員是不在類中的.

虛函式(虛函式表vtable 的實現

乙個動態的鏈結究竟怎樣實現的呢?首先,如果乙個基類中有虛函式,則編譯器自動生成乙個指向虛函式位址的表。有 幾個虛函式,對應的虛函式表 vftable 的長就是幾。然後,在基類中生成乙個指向虛函式表的指標。對應的每個虛函式由編譯器將其對映為虛函式表指標 vfptr 加上乙個數字 這個數 字就是真正的虛...

虛函式表指標,虛函式表

對c 了解的人都應該知道虛函式 virtual function 是通過一張虛函式表 virtual table 來實現的。簡稱為v table。在這個表中,主是要乙個類的虛函式的位址表,這張表解決了繼承 覆蓋的問題,保證其容真實反應實際的函式。這樣,在有虛函式的類的例項中這個表被分配在了 這個例項...

虛函式表實現理念

每個包含了虛函式的類都包含乙個虛表。我們知道,當乙個類 a 繼承另乙個類 b 時,類a會繼承類b的函式的呼叫權。所以如果乙個基類包含了虛函式,那麼其繼承類也可呼叫這些虛函式,換句話說,乙個類繼承了包含虛函式的基類,那麼這個類也擁有自己的虛表。個人理解 用父類指標指向子類物件時,如果基類中有虛函式,那...