【问题标题】:In C++, why does a derived class that just contains a union with an instance of its base class take more memory than the size of the union?在 C++ 中,为什么只包含联合及其基类实例的派生类占用的内存比联合的大小还要多?
【发布时间】:2018-05-12 19:21:47
【问题描述】:

更具体地说,从空类继承的类仅包含一个联合,其成员包括基本无数据类的实例,它占用的内存比联合多。为什么会发生这种情况,有什么办法可以避免消耗额外的内存?

以下代码说明了我的问题:

#include <iostream>

class empty_class { };

struct big : public empty_class
{
    union
    {
        int data[3];
        empty_class a;
    };
};

struct small
{
    union
    {
        int data[3];
        empty_class a;
    };
};

int main()
{   
    std::cout << sizeof(empty_class) << std::endl;
    std::cout << sizeof(big)         << std::endl;
    std::cout << sizeof(small)       << std::endl;
}

当使用 gcc 版本 7.3.0 编译并使用 -std=c++17 编译时,此代码的输出是:

1
16
12

我希望类 bigsmall 应该具有相同的大小;然而奇怪的是,bigsmall 占用更多的内存,尽管它们似乎都包含相同的数据。

同样即使union中数组的大小发生变化,bigsmall的大小之差也是恒定的4个字节。

-编辑:

似乎这种行为并非特定于具有联合数据类型的类。类似的行为发生在派生类具有基类类型的成员的其他类似情况下。感谢指出这一点的人。

【问题讨论】:

  • 这与工会无关。

标签: c++ class inheritance sizeof


【解决方案1】:

这是因为我称之为 C++ 的“唯一身份规则”。 C++ 中特定类型T 的每个(活动)对象必须始终T 类型的每个其他活动对象具有不同的地址。编译器无法为违反此规则的类型提供布局,其中两个具有相同类型T 的不同子对象在其包含对象的布局中具有相同的偏移量。

big 包含两个值得注意的子对象:一个基类empty_class 和一个包含成员empty_class 的匿名联合。

空基类优化基于将空基类的“存储”与其他类型混叠。通常,这是通过为其提供与父类相同的地址来完成的,这意味着该地址通常与第一个非空基类或第一个成员子对象相同。

如果编译器为基类empty_class 提供了与联合成员相同的地址,那么您将拥有该类的两个不同子对象(big::empty_classbig::a),它们具有相同的地址但是不同的对象。

这样的布局会违反唯一标识规则。因此,编译器不能在此处使用空基优化。这也是为什么big 不是标准布局的原因。

【讨论】:

    【解决方案2】:

    union 在这里是一个红鲱鱼。

    如果你简化为

    struct empty{};
    
    struct big : empty
    {
        empty e;
    };
    

    那么sizeof(big) 必须大于sizeof(empty)。这是因为big 中有两个empty 类型的对象,因此它们需要不同的地址。 空基优化不能在这里应用,因为它可以用于

    struct small : empty
    {
        int n;
    };
    

    您可以预期sizeof(small)sizeof(int)

    【讨论】:

      猜你喜欢
      • 2017-12-09
      • 1970-01-01
      • 1970-01-01
      • 2013-01-04
      • 1970-01-01
      • 2012-03-21
      • 2022-08-07
      • 2021-07-08
      • 2011-04-23
      相关资源
      最近更新 更多