【问题标题】:Unexpected size of class in case of virtual inheritance在虚拟继承的情况下类的意外大小
【发布时间】:2019-08-25 19:52:43
【问题描述】:

在涉及虚拟函数的虚拟继承的情况下,有人可以解释一下类的大小。代码如下:

class A
{
public:
    char c;
    virtual void z();
};
class B : public virtual A
{
    double x;
    char a, b;
    int m, n;
public:
    virtual void z();
    virtual int p() { return 0; }
};

B类的大小是48。

【问题讨论】:

  • 没什么好解释的。 C++ 标准不需要任何类的特定大小。
  • 你期望班级的规模是多少,你为什么这么认为?
  • 我期待 B 类的大小为 40,因为在非虚拟继承的情况下,大小为 32。
  • 对于具有虚拟成员或基的类,大小高度依赖于编译器。您的评论建议使用 MSVC 进行编译(预期 +8 字节,改为 +16);如果是这种情况,您可能会惊讶地发现 MSVC 实际上使用两个 vtable 指针而不是一个,我建议 experimenting with class layouts 多一点,启用编译器特定的“显示布局”标志。

标签: c++ inheritance virtual


【解决方案1】:

这取决于您的实施。在您的示例中,在 64 位机器上 sizeof(B) > 32 没有任何理由,除了实施不佳。

在正常实现中,“虚拟”类(具有虚拟方法或基的类)需要每个虚拟基的 vtable 指针(如果有虚拟方法但没有虚拟基,则至少需要一个),并且可能如果在某些情况下有额外的(非虚拟)基础,则需要额外的 vtable 指针。还有一些优化可以减少这种情况(特别是链接时类布局)。

【讨论】:

  • 老实说,任何使sizeof(B) == 32 的实现在 x64 上都是无效的,但在 x86 上应该是有效的。 B 包含 double,这意味着 alignof(B) 至少为 8(因此,[x, a, b, m, n] 在填充后需要 24 个字节)。 Aalignof({pointer}),因此 x64 上为 8 或 x86 上为 4;因此,A 本身需要 16 个字节,如果非虚拟继承,sizeof(B) 至少需要 40 个字节。 [当然,除非重新排序或填充/对齐更改,并且提供的实现使用大致与标准布局类相同的虚拟继承布局规则。]
  • 由于B 有一个虚拟基类,它不需要遵循标准布局,因此可以使用嵌入 A 中的填充作为 1 字节字段,只需 4 个字(32字节)。我会认为任何没有做到这一点的实现都相当糟糕。
  • 我... 认为 这实际上是不合规的,因为它会修改两个类的 class 的 member-specification 部分-说明符。允许实现在用作基类子对象时更改类的布局,是的,但只能在 member-specification 中添加成员(隐式不允许将成员移动到基类中子对象)。默默地将B 成员移动到A 中的编译也会冒破坏有效多态代码的风险,尤其是修改B::c 和/或通过A*A& 调用B::z() 的代码。
  • 也就是说,老实说,我从未见过任何编译器会像这样对非标准类的成员进行真正的重新排序;我什至使用this hack 测试了Compiler Explorer 上的每个编译器(RISC-V Clang 除外,因为缺乏对其实现库的了解);他们都将B 的成员保留在B 中,而A 的填充为空。
  • 嗯...经过进一步研究,如果指定先前成员具有[[no_unique_address]] 属性,则允许将类成员存储在先前成员的填充中,因此在这种情况下,我会说您是完全正确,前提是元素保持指定的顺序。
猜你喜欢
  • 2012-05-19
  • 2013-06-21
  • 2012-07-10
  • 2023-03-22
  • 2017-12-12
  • 1970-01-01
  • 2020-10-31
  • 2013-02-26
相关资源
最近更新 更多