当前位置:  开发笔记 > 编程语言 > 正文

有关C++中多重继承,虚拟基类和对象大小的问题

如何解决《有关C++中多重继承,虚拟基类和对象大小的问题》经验,为你挑选了2个好方法。

以下代码打印20,即sizeof(z)为20.

#include 
class 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.

#include 
class 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将为空.

推荐阅读
重庆制造漫画社
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有