以下代码打印20,即sizeof(z)为20.
#includeclass Base { public: int a; }; class X:virtual public Base { public: int x; }; class Y:virtual public Base { public: int y; }; class Z:public X,public Y { }; int main() { Z z; cout << sizeof(z) < 如果我不在这里使用虚拟基类,即对于以下代码:sizeof(z)是16.
#includeclass Base { public: int a; }; class X:public Base { public: int x; }; class Y:public Base { public: int y; }; class Z:public X,public Y { }; int main() { Z z; cout << sizeof(z) < 为什么sizeof(z)在第一种情况下更多(20)?不应该是12,因为Base只会在Z中包含一次吗?
1> markets..:让我们看看这两种情况的类布局.
如果没有虚拟,你有两个基类("X"和"Y"),每个类都有一个整数,每个类都集成了一个"Base"基类,它也有一个整数.这是4个整数,每个32位,总共16个字节.
Offset Size Type Scope Name 0 4 int Base a 4 4 int X x 8 4 int Base a 12 4 int Y y 16 size (Z members would come at the end)(编辑:我已经在DJGPP中编写了一个程序来获取布局并调整表格来解释它.)
现在让我们谈谈虚拟基类:它们用指向共享实例的指针替换类的实际实例.你的"Z"类只有一个"Base"类,"X"和"Y"的实例都指向它.因此,你有X,Y和Z中的整数,但你只有一个Z.这意味着你有三个整数,或12个字节.但是X和Y也有一个指向共享Z的指针(否则他们不知道在哪里找到它).在32位机器上,两个指针将增加8个字节.这总计你看到的20.内存布局可能看起来像这样(我还没有验证它...... ARM有一个例子,其中排序是X,Y,Z,然后是Base):
Offset Size Type Scope Name Value (sort of) 0 4 Base offset X ? 16 (or ptr to vtable) 4 4 int X x 8 4 Base offset Y ? 16 (or ptr to vtable) 12 4 int Y y 16 4 int Base a 20 size (Z members would come before the Base)因此,内存差异是两件事的组合:一个较少的整数和两个以上的指针.与另一个答案相反,我不相信vtables支付任何(编辑)直接(/编辑)卷,因为没有虚函数.
编辑:ppinsider提供了有关gcc案例的更多信息,其中他演示了gcc通过使用其他空的vtable(即没有虚函数)来实现指向虚基类的指针.这样,如果有虚函数,则在类实例中不需要额外的指针,需要更多内存.我怀疑缺点是进入基类的额外间接.
我们可能希望所有编译器都这样做,但也许不会.在ARM第225页讨论不提虚函数表虚基类.第235页专门针对"具有虚函数的虚拟基类",并且有一个图表,指示存储器布局,其中有来自X和Y部分的指针,这些指针与指向vtable的指针是分开的.我建议任何人不要理所当然地认为Base的指针将以表的形式实现.
2> user23167..:马克桑特森的回答几乎都是钱,但断言没有vtables是不正确的.您可以使用g ++ -fdump-class-hierarchy来显示正在发生的事情.这是no virtuals案例:
Class Base size=4 align=4 base size=4 base align=4 Base (0x19a8400) 0 Class X size=8 align=4 base size=8 base align=4 X (0x19a8440) 0 Base (0x19a8480) 0 Class Y size=8 align=4 base size=8 base align=4 Y (0x19a84c0) 0 Base (0x19a8500) 0 Class Z size=16 align=4 base size=16 base align=4 Z (0x19b1800) 0 X (0x19a8540) 0 Base (0x19a8580) 0 Y (0x19a85c0) 8 Base (0x19a8600) 8要特别注意"基本大小"参数.现在是虚拟案例,只显示Z:
Class Z size=20 align=4 base size=16 base align=4 Z (0x19b3000) 0 vptridx=0u vptr=((& Z::_ZTV1Z) + 12u) X (0x19a8840) 0 primary-for Z (0x19b3000) subvttidx=4u Base (0x19a8880) 16 virtual vbaseoffset=-0x0000000000000000c Y (0x19a88c0) 8 subvttidx=8u vptridx=12u vptr=((& Z::_ZTV1Z) + 24u) Base (0x19a8880) alternative-path注意"基本大小"是相同的,但"大小"是一个指针更多,并注意现在有一个vtable指针!这反过来包含父类的构造vtable,以及所有类间魔术(构造vtable和虚拟表表(VTT)),如下所述:
http://www.cse.wustl.edu/~mdeters/seminar/fall2005/mi.html
请注意,实际的函数dispatch vtable将为空.