1. 程式人生 > >虛擬函式表存在的位置,初始化時機

虛擬函式表存在的位置,初始化時機

轉自:http://blog.csdn.net/lingfengtengfei/article/details/12345809

1.虛擬函式

·虛表是怎麼實現的?虛表存放在哪裡?

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

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

我們很難通過 C++語言本身來找到答案。 C++標準給編譯器實現者定義了語法規範,但是被並沒有定義如何實現這些語法規範,不同的編譯器實現者可能有不同的實現方法,可以肯定的是他們的編譯器必須符合這些語法規範。組合語言作為最接近機器語言的計算機語言,可以為我們揭示一些隱藏在編譯器內部的細節。接下來本來就試圖通過對 C++原始碼進行反彙編的方式來解答這些疑惑。

二、分析

這裡我選用 WinXP 和 VS2008 作為我們這次分析的平臺。我們建立一個最簡單的 Win32 控制檯程式,並定義兩個簡單的類:


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


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

·擁有虛擬函式的類會有一個虛表,而且這個虛表存放在類定義模組的資料段中。模組的資料段通常存放定義在該模組的全域性資料和靜態資料,

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

·類的虛表會被這個類的所有物件所共享。類的物件可以有很多,但是他們的虛表指標都指向同一個虛表,從這個意義上說,我們可以把虛表簡單理解為類的靜態資料成員。值得注意的是,雖然虛表是共享的,但是虛表指標並不是,類的每一個物件有一個屬於它自己的虛表指標。

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

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

 

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

this" 指 針 , 同 時 它 的 值 又 賦 給 了 寄 存 器 EAX。"[email protected]@[email protected]"就是上面提到的虛表,同時它也代表了虛表的地址。

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

1.進入到建構函式體之間。在這個階段如果存在虛擬函式的話,虛表指標被初始化。如果存在建構函式的初始化列表的話,初始化列表也會被執行。

2.進入到建構函式體內。這一階段是我們通常意義上說的建構函式

簡單的搞個基類Base{void fun();virtual void print(){...};public:int a;static b;}

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

類中只有虛表指標和普通成員(包括const成員)而普通函式,靜態成員是不在類中的.