更具体地说,一个类继承自一个空类,只包含一个 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
我希望类 big 和 small 应该具有相同的大小;然而奇怪的是,big 比 small 占用更多的内存,尽管它们看起来包含相同的数据。
同样,即使 union 中的数组大小发生变化,big 和 small 的大小之间的差异也是一个常量 4 字节。
-编辑:
似乎这种行为并不特定于具有 union 数据类型的类。类似的行为发生在派生类具有基类类型的成员的其他类似情况中。感谢那些指出这一点的人。
最佳答案
这是因为我称之为 C++ 的“唯一身份规则”。 C++ 中特定类型 T
的每个(事件)对象 必须始终 具有与类型 T
的所有其他事件对象不同的地址。编译器无法为违反此规则的类型提供布局,其中具有相同类型 T
的两个不同子对象在其包含对象的布局中具有相同的偏移量。
类 big
包含两个值得注意的子对象:一个基类 empty_class
和一个包含成员 empty_class
的匿名 union 。
空基优化是基于将空基类的“存储”与其他类型别名化。通常,这是通过为它提供与父类相同的地址来完成的,这意味着该地址通常与第一个非空基类或第一个成员子对象相同。
如果编译器为基类 empty_class
提供了与 union 成员相同的地址,那么您将拥有该类的两个不同的子对象(big::empty_class
和 big::a
) 具有相同的地址但是是不同的对象。
这样的布局会违反唯一身份规则。因此,编译器不能在这里使用空基优化。这也是 big
不是标准布局的原因。
关于c++ - 在 C++ 中,为什么仅包含 union 及其基类实例的派生类占用的内存大于 union 的大小?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50309913/