在 C++ 中,为啥只包含联合及其基类实例的派生类占用的内存比联合的大小还要多?
Posted
技术标签:
【中文标题】在 C++ 中,为啥只包含联合及其基类实例的派生类占用的内存比联合的大小还要多?【英文标题】: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
我希望类 big 和 small 应该具有相同的大小;然而奇怪的是,big 比 small 占用更多的内存,尽管它们似乎都包含相同的数据。
同样即使union中数组的大小发生变化,big和small的大小之差也是恒定的4个字节。
-编辑:
似乎这种行为并非特定于具有联合数据类型的类。类似的行为发生在派生类具有基类类型的成员的其他类似情况下。感谢指出这一点的人。
【问题讨论】:
这与工会无关。 【参考方案1】:这是因为我称之为 C++ 的“唯一身份规则”。 C++ 中特定类型T
的每个(活动)对象必须始终与T
类型的每个其他活动对象具有不同的地址。编译器无法为违反此规则的类型提供布局,其中两个具有相同类型T
的不同子对象在其包含对象的布局中具有相同的偏移量。
类big
包含两个值得注意的子对象:一个基类empty_class
和一个包含成员empty_class
的匿名联合。
空基类优化基于将空基类的“存储”与其他类型混叠。通常,这是通过为其提供与父类相同的地址来完成的,这意味着该地址通常与第一个非空基类或第一个成员子对象相同。
如果编译器为基类empty_class
提供了与联合成员相同的地址,那么您将拥有该类的两个不同子对象(big::empty_class
和big::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)
。
【讨论】:
以上是关于在 C++ 中,为啥只包含联合及其基类实例的派生类占用的内存比联合的大小还要多?的主要内容,如果未能解决你的问题,请参考以下文章