【问题标题】:Question on multiple inheritance, virtual base classes, and object size in C++关于 C++ 中的多重继承、虚拟基类和对象大小的问题
【发布时间】:2010-09-28 14:54:36
【问题描述】:

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

#include <iostream.h>
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) <<endl;
}

而如果我在这里不使用虚拟基类,即对于以下代码: sizeof(z) 是 16。

#include <iostream.h>
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) <<endl;
}

为什么在第一种情况下 sizeof(z) more(20) ? 不应该是 12,因为 Base 将被包括在内 在 Z 中只有一次?

【问题讨论】:

    标签: c++ multiple-inheritance sizeof


    【解决方案1】:

    额外的大小可能是因为虚拟类和多重继承分配了额外的 VTables (http://en.wikipedia.org/wiki/Vtable)。

    【讨论】:

      【解决方案2】:

      我们来看看这两个案例的类布局。

      没有虚拟,你有两个基类(“X”和“Y”),每个基类都有一个整数,每个类都集成了一个“基”基类,它也有一个整数。那是 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 页讨论了虚拟基类,但未提及 vtable。第 235 页专门针对“具有虚拟函数的虚拟基类”,并有一个图表指示内存布局,其中 X 和 Y 部分的指针与指向 vtable 的指针是分开的。我建议任何人不要想当然地认为指向 Base 的指针将以表的形式实现。

      【讨论】:

      • 马克,你是如何生成这个布局的?
      • 如果你的意思是表格格式,它只是代码。我只是猜测的内容。我将编辑我的答案,提供更多详细信息并参考 ppinsider 的答案。
      【解决方案3】:

      Mark Santesson 的回答几乎是在金钱上,但没有 vtables 的断言是不正确的。您可以使用 g++ -fdump-class-hierarchy 来显示正在发生的事情。这是没有虚拟的情况:

      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
      

      注意“base size”是一样的,但是“size”多了一个指针,注意现在多了一个vtable指针!这又包含父类的构造 vtable,以及所有类间魔法(构造 vtable 和虚拟表 (VTT)),如下所述:

      http://www.cse.wustl.edu/~mdeters/seminar/fall2005/mi.html

      请注意,实际的函数调度 vtable 将为空。

      【讨论】:

      • 但是如果没有一个类有虚函数,为什么是 vtable?
      • 使用该选项通过 g++ 运行代码,您就会明白为什么。查看我提供的链接以了解所有内容;比我在这里重新散列要好得多!
      • “base size=16 base align=4”中的“base”是什么意思?是指所有的基类吗?
      猜你喜欢
      • 2019-12-20
      • 1970-01-01
      • 2012-10-11
      • 2023-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-20
      • 2017-03-31
      相关资源
      最近更新 更多