2014-02-20 44 views
4

我對VIRTUAL關鍵詞感到困惑。我試圖找出編譯器如何在內存中實現這​​一點。 好吧,讓我解釋一下例子。我正在使用Microsoft Visual Studio 2010作爲虛擬依賴編譯器的實現。C++編譯器如何在內存中實現虛擬繼承?

這裏是第一代碼

#include<iostream> 
class one 
{ 
    int _a; 
public: 
    virtual ~one(){} 
}; 
class two:public one 
{ 
    int _a; 
public: 
    virtual ~two(){} 
}; 

int main() 
{ 
    using namespace std; 
    cout<<"sizeof two="<<sizeof(two)<<endl; 
    return 0; 
} 

O/P是因爲_ 的vptr _TWO,一個:: _和兩個:: _一個

這裏12個字節,是另一個示例代碼

#include<iostream> 
class one 
{ 
    int _a; 
public: 
    virtual ~one(){} 
}; 
class two 
{ 
    int _a; 
public: 
    virtual ~two(){} 
}; 
class three:virtual public one,virtual public two 
{ 
}; 

int main() 
{ 
    using namespace std; 
    cout<<"sizeof three="<<sizeof(three)<<endl; 
    return 0; 
} 

在這種情況下o/p是20字節:O怎麼回事?請解釋!!據我說,它應該是16個字節。 __vptr_three(指向vtable的指針),_vptr1_three(指向虛擬基類表的指針),one :: _ a和two :: _ a。爲什麼要維護虛擬基類表?

+0

注意:你的虛擬基類的例子是......很奇怪。理論上,虛擬繼承用於解決*鑽石問題*:D從C和B繼承,C和B都從A繼承。您沒有鑽石,因此虛擬繼承在您的情況下無用。 –

+0

@MatthieuM。你的觀點是相關的馬特,但我不想實施任何我只是學習:),當你學習你應該學習所有方面! –

回答

7

pdf這個pdf包含了你需要了解的一切,以及編譯器的作者如何在VC++中實現虛擬繼承。

下面是的class three構造的拆裝,

00A516BD cmp   dword ptr [ebp+8],0 
00A516C1 je   three::three+60h (0A516F0h) 
00A516C3 mov   eax,dword ptr [this] 
00A516C6 mov   dword ptr [eax],offset three::`vbtable' (0A57828h) => 4 Bytes 
00A516CC mov   ecx,dword ptr [this] 
00A516CF add   ecx,8 
00A516D2 call  one::one (0A51253h) 
00A516D7 or   dword ptr [ebp-0D4h],1 
00A516DE mov   ecx,dword ptr [this] 
00A516E1 add   ecx,10h 
00A516E4 call  two::two (0A512BCh) 
00A516E9 or   dword ptr [ebp-0D4h],2 
00A516F0 mov   eax,dword ptr [this] 
00A516F3 mov   ecx,dword ptr [eax] 
00A516F5 mov   edx,dword ptr [ecx+4] 
00A516F8 mov   eax,dword ptr [this] 
00A516FB mov   dword ptr [eax+edx],offset three::`vftable' (0A57820h) => 4 Bytes 
00A51702 mov   eax,dword ptr [this] 
00A51705 mov   ecx,dword ptr [eax] 
00A51707 mov   edx,dword ptr [ecx+8] 
00A5170A mov   eax,dword ptr [this] 
00A5170D mov   dword ptr [eax+edx],offset three::`vftable' (0A57814h) => 4 Bytes 
00A51714 mov   eax,dword ptr [this] 
00A51717 pop   edi 
00A51718 pop   esi 
00A51719 pop   ebx 
00A5171A add   esp,0D8h 

正如你可以看到上面,
虛擬基表指針需要4個字節,(vbtable)
2虛擬函數表指針需要4 * 2 = 8個字節,(vftable)
的構件one::_a需要4個字節
的構件two::_a需要4個字節

因此在所有20個字節。 原因2虛擬函數表指針在PDF(第17頁)如下給出,

「在Visual C++,以避免昂貴的轉換到虛擬基P取出一個vftable條目時,T的新的虛擬功能,在新的vftable中接收條目,需要在T的頂部引入新的vfptr。「

+1

真棒PDF :),我正在尋找這個怪物,非常感謝! –

+4

我想,如果這個pdf將被刪除,你應該從pdf中添加一些段落到答案。 – soon

+1

事實上,只有鏈接的答案是因爲它們傾向於鏈接腐爛而被忽視。實際上鼓勵提供*參考*來補充您的答案;然而,堆棧溢出(如果僅用於廣泛筆畫),應立即訪問答案的肉。這也將是有益的,如果你解釋爲什麼20 ... –

2

這完全是特定於實現的,編譯器可以隨意組織任何它喜歡的方式(並使所生成的類爲任意大小),從而提供所有工作。從外觀上看,我認爲類three包含三個隱藏的指針(系統上每個4個字節):一個「虛擬基址指針」,包含虛擬基地址在three內,一個虛擬函數指針包含one中的虛擬函數的地址,以及包含two中的虛擬函數的地址的第二虛擬函數指針。

因此,指針的3 * 4字節,以及基類的兩個int成員的2 * 4字節,總共20個字節。當然,如果你要在一個64位機器上(8字節指​​針)編譯這段代碼,那麼你會得到一些不同的東西:至少32字節,但如果編譯器認爲它需要填寫int成員。

+0

這是我也懷疑,但爲什麼兩個虛擬函數指針? –