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的函式的呼叫權。所以如果乙個基類包含了虛函式,那麼其繼承類也可呼叫這些虛函式,換句話說,乙個類繼承了包含虛函式的基類,那麼這個類也擁有自己的虛表。個人理解 用父類指標指向子類物件時,如果基類中有虛函式,那...