虛函式是c++語言實現執行時多型的唯一手段,因此掌握c++虛函式也成為c++程式設計師是否合格的試金石。csdn網友所發的一篇博文《vc虛函式布局引發的問題》 從彙編角度分析了物件虛函式表的構,以及c++指標或者引用是如何利用這個表來實現執行時多型。
誠然,c++虛函式的結構會因編譯器不同而異,但所使用的原理是一樣的。為此,本文使用linux平台下的g++編譯器,試圖從彙編的層面上分析虛函式表的結構,以及如何利用它來實現執行時多型。
組合語言是難讀的,特別是對一些沒有彙編基礎的朋友,因此,本文將彙編翻譯成相應的c語言,以方便讀者分析問題。
1. **
2. 兩個類的虛函式表(vtable)
使用g++ –wall –s test.cpp命令,可以將上述的c++**生成它相應的彙編**。
_ztv4base是乙個資料符號,它的命名規則是根據g++的內部規則來命名的,如果你想檢視它真正表示c++的符號名,可使用c++filt命令來轉換,例如:
[lyt@t468 ~]$ c++filt _ztv4base
vtable for base
_ztv4base符號(或者變數)可看作為乙個陣列,它的第一項是0,第二項_zit4base是關於base的型別資訊,這與typeid有關。為方便討論,我們略去此二項資料。 因此base類的vtable的結構,翻譯成相應的c語言定義如下:
而derive的更是類似,只有稍為有點不同:
相應的c語言定義如下:
從上面兩個類的vtable可以看到,derive的vtable中的第一項重寫了base類vtable的第一項。只要子類重寫了基類的虛函式,那麼子類vtable相應的項就會更改父類的vtable表項。 這一過程是編譯器自動處理的,並且每個的類的vtable內容都放在資料段裡面。
3. 誰讓物件與vtable綁到一起
上述**只是定義了每個類的vtable的內容,但我們知道,帶有虛函式的物件在它內部都有乙個vtable指標,指向這個vtable,那麼是何時指定的呢? 只要看看建構函式的彙編**,就一目了然了:
base::base()函式的編譯**如下:
zn4basec1ev這個符號是c++函式base::base() 的內部符號名,可使用c++flit將它還原。c++裡的class,可以定義資料成員,函式成員兩種。但轉化到彙編層面時,每個物件裡面真正存放的是資料成員,以及虛函式表。
在上面的base類中,由於沒有資料成員,因此它只有乙個vtable指標。故base類的定義,可以寫成如下相應的c**:
建構函式中最關鍵的兩句是:
movl 8(%ebp), %eax
movl $_ztv4base+8, (%eax)
$_ztv4base+8 就是base類的虛函式表的開始位置,因此,建構函式對應的c**如下:
同樣地,derive類的建構函式如下:
4. 實現執行時多型的最關鍵一步
在造構函式裡面設定好的vtable的值,顯然,同一型別所有物件內的vtable值都是一樣的,並且永遠不會改變。下面是main函式生成的彙編**,它展示了c++如何利用vtable來實現執行時多型。
andl $-16, %esp
subl $32, %esp
這兩句是為區域性變數d和bp在堆疊上分配空間,也即如下的語句:
derive d;
base *pb;
leal 24(%esp), %eax
movl %eax, (%esp)
call _zn6derivec1ev
derive::dervice(&d);
leal 24(%esp), %eax
movl %eax, 28(%esp)
這裡其實是將&d的值賦給pb,也即:
pb = &d;
最關鍵的**是下面這一段:
翻譯成c語言也就傳神的那句:
pb->vtable[0](bp);
編譯器會記住f虛函式放在vtable的第0項,這是編譯時資訊。
5. 小結
這裡省略了很多關於編譯器和c++的細枝未節,是出於討論方便用的需要。從上面的編譯**可以看到以下資訊:
1.每個類都有各有的vtable結構,編譯會正確填寫它們的虛函式表
2. 物件在建構函式時,設定vtable值為該類的虛函式表
3.在指標或者引用時呼叫虛函式,是通過object->vtable加上虛函式的offset來實現的。
當然這僅僅是g++的實現方式,它和vc++的略有不同,但原理是一樣的。
C 筆記 深度剖析多型(虛函式表)上
摘要 多型性是物件導向程式設計語言中資料抽象和繼承之外的第三個基本特性。要想認識多型,我們需要從最基礎的知識開始著手,這篇部落格是我整理了很久才發出來的,裡面對於多型的底層分析很詳盡,希望可以對你們有所幫助 多型的概念 多型,顧名思義就是一種事物具有多種形態,用比較正式的話來說,大概就是下面這段話啦...
C 虛函式表剖析
為了實現c 的多型,c 使用了一種動態繫結的技術。這個技術的核心是虛函式表 下文簡稱虛表 本文介紹虛函式表是如何實現動態繫結的。每個包含了虛函式的類都包含乙個虛表。我們知道,當乙個類 a 繼承另乙個類 b 時,類a會繼承類b的函式的呼叫權。所以如果乙個基類包含了虛函式,那麼其繼承類也可呼叫這些虛函式...
C 虛函式表剖析
為了實現c 的多型,c 使用了一種動態繫結的技術。這個技術的核心是虛函式表 下文簡稱虛表 本文介紹虛函式表是如何實現動態繫結的。每個包含了虛函式的類都包含乙個虛表。我們知道,當乙個類 a 繼承另乙個類 b 時,類a會繼承類b的函式的呼叫權。所以如果乙個基類包含了虛函式,那麼其繼承類也可呼叫這些虛函式...