C 之虛函式和多重繼承

2021-06-26 22:56:20 字數 1984 閱讀 1867

原文出處:原文的作者是aaron ballman,voting member of the c++ standards committee.

class a 

virtual void foo() };

class b

virtual void bar() };

class c : public a, public b

void foo()

};

在這段**中有3個類,類a擁有乙個int型成員變數和乙個虛函式foo(),類b擁有乙個double型成員變數和乙個虛函式bar(),類c繼承於a和b,擁有乙個指向字元(或字串)的指標,且重新定義了從a中繼承得到的foo()。a、b、c的物件的記憶體布局如下:

可以看到c物件將包含a類子物件和b類子物件,其中a類子物件和b類子物件被分別插入了乙個指向虛函式表的指標。假設現在我們有乙個c類物件 c c; ,那麼c中的內容可以用下圖表示:

c的首位址是0x00045032,在該位址上有乙個void*型的指標,該指標指向虛函式表,即它的內容就是虛函式表的位址,0x0004503a處也是乙個指標,該指標也指向乙個虛函式表(次虛函式表),即該指標的內容是次函式表的位址(可以看到主虛函式表和次虛函式表並不在一起,有的編譯器會把它們放在一起),下面我們可以看看這兩張虛函式表的內容:

主虛函式表中記錄了c::foo的位址,次虛函式表中記錄了b::bar的位址,同時這裡可以看到,常量字串也放在不遠的位置,s指向了這個位置。趁熱打鐵,我們可以看看實際呼叫背後的彙編**!

c->foo();// #1

c->bar();// #2

在x86系統上,呼叫c++函式一般遵循以下三個規則:

1)把this指標的值存在ecx暫存器中;

2)如果有返回值的話,把返回值存在eax暫存器;

3)函式引數從右向左依次壓入棧中;

這樣,#1大概可以轉換成如下的彙編指令:

mov eax, 1    ; load the function pointer

mov ecx, c ; load the this pointer

call eax ; call the function

因為foo()就在主虛函式表中,所以組合語言相對簡單,#2的彙編指令就要複雜一些了,裡面涉及了給this指標調整offset,稱為

thunk:

mov edx, c        ; get the instance pointer

add edx, 8 ; advance by 8 bytes

mov eax, [edx] ; load the function pointer

mov ecx, edx ; load the this pointer

call eax ; call the function

前兩條指令就是調整this指標,第4條指令將調整後的this指標放入ecx中。以下是幾點結論:

1)類中如果有虛函式,則會有相應的虛函式表,虛函式表中的資訊在編譯時就會確定;

2)乙個類可能有多個虛函式表(虛函式表指標);

3)編譯器有時候需要插入thunk來保證this指標指向合適的位置;

4)使用虛函式會付出一些空間和複雜度的代價,使用多重繼承下的虛函式付出的代價更多,如果效率很重要,可以考慮不依賴虛函式。

c 虛函式 多重繼承

如果類有虛函式,則類會有預設的乙個指標成員指向虛函式表 可能儲存在全域性區 的位址,指標占用4個位元組。虛函式 easytest.cpp 定義控制台應用程式的入口點。include stdafx.h include using namespace std class a class b public...

c 多重繼承和虛繼承

多重繼承 多重繼承是指從多個直接基類中產生派生類的能力。多重繼承的派生類繼承了所有父類的屬性。struct base1 struct base2 struct d1 public base1,public base2 d1 default 虛繼承 虛繼承的目的是令某個類做出宣告,承諾願意共享它的基類...

C 多重繼承 虛繼承

c 中的多繼承,建構函式處理並沒有問題,物件構造的時候按照繼承中宣告的順序呼叫多個父類的建構函式,析構函式同樣遵守單繼承中的原則。二意性問題 如果多基類中存在同名成員,會產生二意性的問題 比如,root1類中宣告doany 介面,root2類中也宣告了doany 介面,child多承繼root1和r...