C++编译器如何在内存中实现虚拟继承?

问题描述:

我对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。你的观点是相关的马特,但我不想实施任何我只是学习:),当你学习你应该学习所有方面! –

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 ... –

这完全是特定于实现的,编译器可以随意组织任何它喜欢的方式(并使所生成的类为任意大小),从而提供所有工作。从外观上看,我认为类three包含三个隐藏的指针(系统上每个4个字节):一个“虚拟基址指针”,包含虚拟基地址在three内,一个虚拟函数指针包含one中的虚拟函数的地址,以及包含two中的虚拟函数的地址的第二虚拟函数指针。

因此,指针的3 * 4字节,以及基类的两个int成员的2 * 4字节,总共20个字节。当然,如果你要在一个64位机器上(8字节指​​针)编译这段代码,那么你会得到一些不同的东西:至少32字节,但如果编译器认为它需要填写int成员。

+0

这是我也怀疑,但为什么两个虚拟函数指针? –