c++ - 在 C++ 中,为什么仅包含 union 及其基类实例的派生类占用的内存大于 union 的大小?

标签 c++ class inheritance sizeof

更具体地说,一个类继承自一个空类,只包含一个 union 体,其成员包括基本无数据类的实例,比 union 体占用更多的内存。为什么会发生这种情况,是否有任何方法可以避免消耗额外的内存?

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

#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 编译(虽然,我使用 c++11 和 c++ 得到相同的结果14), 是:

1
16
12

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

同样,即使 union 中的数组大小发生变化,bigsmall 的大小之间的差异也是一个常量 4 字节。

-编辑:

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

最佳答案

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

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

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

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

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

关于c++ - 在 C++ 中,为什么仅包含 union 及其基类实例的派生类占用的内存大于 union 的大小?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50309913/

相关文章:

c++ - 省略号 try catch on c++

Java 动态绑定(bind)失败

c++ - 将 Boost 序列化与前向声明类和继承一起使用

java - 实体 Sprite 怪物实体实例...就这么简单但不起作用?

java - 如何使 <T extends E> 包含泛型类型参数?

c++ - 'this' 指针的类型

c++ - C++ 有没有像 getdelim 这样的函数?

python - 使用cython将c++中的helloworld包装到python中时出错

c# - 如何指定可能包含另一个类的类

java - 如何用delphi xe5扩展android类