【问题标题】:Size of the classes in case of virtual inheritance在虚拟继承的情况下类的大小
【发布时间】:2012-05-19 10:44:28
【问题描述】:

在涉及虚函数的虚拟继承的情况下,谁能解释一下类的大小。

   class A{
          char k[ 3 ];
          public:
          virtual void a(){};
          };

   class B : public  A{
          char j[ 3 ];
          public:
          virtual  void b(){};
          };

   class C : public virtual A{
          char i[ 3 ];
          public:
          virtual void c(){};
          };

   class D : public B, public C{
          char h[ 3 ];
          public:
          virtual void d(){};
          };

类大小的输出是:

    sizeof(A): 8
    sizeof(B): 12
    sizeof(C): 16
    sizeof(D): 32

我使用的编译器是 gcc version 4.6.1 (Ubuntu/Linaro 4.6.1-9ubuntu3)

【问题讨论】:

  • 将 vptr 的大小添加到班级的大小中。更多详情 -> stackoverflow.com/questions/1604176/size-of-virtual-pointer-c
  • 这些尺寸有什么让你吃惊的地方?
  • 请记住,所有非空基础子对象都必须正确对齐。因此,如果您愿意,您可以考虑每个类都有一个 4 字节的成员对齐在 4。
  • @FredLarson...我知道多态类受虚拟指针影响的情况下的大小...但是虚拟继承的影响是什么?当我们虚拟继承时是否有任何隐藏指针??
  • @DumbCoder:没那么简单。虚拟表需要vptr,但虚拟基需要额外的指针。

标签: c++ polymorphism virtual


【解决方案1】:

大小(A):8

数组中 3 个字节,1 个字节填充,4 个字节用于 vptr(指向 vtable 的指针)

大小(B):12

一个子对象:8 个,3 个字节用于额外数组,1 个字节填充

大小(C):16

这可能会让你感到惊讶... 一个子对象:8 个,3 个字节用于额外数组,1 个字节填充,4 个字节指向 A 的指针

当你有虚拟继承时,虚拟基础子对象相对于完整类型开始的位置是未知的,因此在原始对象中添加了一个额外的指针来跟踪虚拟基础的位置。考虑这个例子:

struct A {};
struct B : virtual A {};
struct C : virtual A {};
struct D : B, C {};

当完整类型是 B 时,A 相对于 B 对象开始的位置可以不同于 B 的子对象 A 的位置最终对象D。如果这不明显,假设相对位置相同,并检查A相对于CC最终对象或C子对象中D中的相对位置是否也可以得到维护。

至于最后一个例子,我不太想分析它……但你可以阅读Itanium C++ ABI 了解 C++ 对象模型的具体实现。所有其他实现没有太大区别。


最后一个例子:

大小(D):32

D 包含一个 B 子对象 (12) 和一个 C 子对象 (16),加上一个大小为 3 的附加数组和一个额外的填充位 1。

在这种特殊情况下,可能会出现的问题是,如果C 虚拟继承自A,为什么会有两个A 子对象,答案是虚拟基础意味着对象愿意共享这个base 与层次结构中也愿意共享它的任何其他类型。但在这种情况下,B 不愿意分享它的A 子对象,所以C 需要它自己的。

您应该能够通过将日志添加到不同级别的构造函数来跟踪这一点。对于A,让它在编译器中取一个值并从每个扩展类传递不同的值。

【讨论】:

    【解决方案2】:

    sizeof(C)sizeof(B) 更多,因为 C 类型的对象(因为它实际上是从 A 继承的)将包含一个指针(除了 B 类型的对象也将包含的 vptr)指向它从 A. Scott Meyers 继承的本身在他的书 More Effective C++ 的第 24 条中详细解释了这一点(大约 10 页):“了解虚函数、多重继承、虚拟基类和 RTTI 的成本”

    【讨论】:

      【解决方案3】:

      要知道数据结构的实际大小,您可以说编译器不要使用#pragma pack(1) 在内存中对齐它。要保存当前打包设置并在以后恢复它们,您还可以使用#pragma pack(push) 和#pragma pack(pop)。

      【讨论】:

        【解决方案4】:

        这是我对所有字节的使用位置的最佳猜测:

                      Avptr Bvptr CVptr DVptr k j i h k' j' i'  TOTAL
        ============= ========================================= =====
        sizeof(A): 8    4                     4                   8
        sizeof(B): 12        4                4 4                12
        sizeof(C): 16              4          4 4 4              16
        sizeof(D): 32                    4    4 4 4 4  4  4  4   32
        

        地点:

        • 每个 vptr 占用 4 个字节(64 位指针)
        • char 数组每个占用 4 个字节(四舍五入以便对齐)
        • k'、j' 和 i' 是那些通过 C 而不是 B 继承的变量的副本。

        【讨论】:

        • C 没有扩展 B,所以它不包含 j。额外的 4 有一个指向 A 子对象的指针
        猜你喜欢
        • 1970-01-01
        • 2013-06-21
        • 2023-03-22
        • 2017-12-12
        • 1970-01-01
        • 2020-10-31
        • 2013-02-26
        • 2012-07-10
        相关资源
        最近更新 更多