在 C++ 中,为什么仅包含一个联合及其基 class 实例的派生 class 占用的内存多于联合的大小?
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?
更具体地说,一个 class,继承自一个空 class,只包含一个联合,其成员包括基本无数据 class 的实例,占用更多内存不仅仅是工会。为什么会发生这种情况,是否有任何方法可以避免消耗额外的内存?
下面的代码说明了我的问题:
#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
我希望 classes big 和 small 的大小应该相同;然而奇怪的是,big 比 small 占用更多的内存,尽管它们看起来包含相同的数据。
同样即使union中数组的大小发生变化,big和small的大小相差一个常量 4 个字节。
-编辑:
似乎此行为并非特定于具有联合数据类型的 classes。在派生 class 具有基本 class 类型的成员的其他类似情况下,也会出现类似的行为。感谢指出这一点的人。
这是因为我称之为 C++ 的 "unique identity rule"。 C++ 中特定类型 T
中的每个(活动)对象必须始终 具有与类型 T
的每个其他活动对象不同的地址。编译器无法为违反此规则的类型提供布局,其中具有相同类型 T
的两个不同子对象在其包含对象的布局中具有相同的偏移量。
Class big
包含两个值得注意的子对象:基 class empty_class
和包含成员 empty_class
的匿名联合。
空基数优化基于将空基数 class 的 "storage" 与其他类型别名化。通常,这是通过为它提供与父对象相同的地址 class 来完成的,这意味着该地址通常与第一个非空基或第一个成员子对象相同。
如果编译器为基 class empty_class
提供了与联合成员相同的地址,那么您将拥有 class 的两个不同的子对象(big::empty_class
和big::a
) 具有相同的地址但不同的对象。
这样的布局会违反唯一身份规则。因此,编译器不能在这里使用空基优化。这也是 big
不是标准布局的原因。
union
在这里是一条红鲱鱼。
如果简化为
struct empty{};
struct big : empty
{
empty e;
};
则sizeof(big)
必须大于sizeof(empty)
。这是因为 big
中有两个 empty
类型的对象,因此它们需要不同的地址。 空基优化不能在这里应用,因为它可以用于
struct small : empty
{
int n;
};
您可以期望 sizeof(small)
成为 sizeof(int)
。
更具体地说,一个 class,继承自一个空 class,只包含一个联合,其成员包括基本无数据 class 的实例,占用更多内存不仅仅是工会。为什么会发生这种情况,是否有任何方法可以避免消耗额外的内存?
下面的代码说明了我的问题:
#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
我希望 classes big 和 small 的大小应该相同;然而奇怪的是,big 比 small 占用更多的内存,尽管它们看起来包含相同的数据。
同样即使union中数组的大小发生变化,big和small的大小相差一个常量 4 个字节。
-编辑:
似乎此行为并非特定于具有联合数据类型的 classes。在派生 class 具有基本 class 类型的成员的其他类似情况下,也会出现类似的行为。感谢指出这一点的人。
这是因为我称之为 C++ 的 "unique identity rule"。 C++ 中特定类型 T
中的每个(活动)对象必须始终 具有与类型 T
的每个其他活动对象不同的地址。编译器无法为违反此规则的类型提供布局,其中具有相同类型 T
的两个不同子对象在其包含对象的布局中具有相同的偏移量。
Class big
包含两个值得注意的子对象:基 class empty_class
和包含成员 empty_class
的匿名联合。
空基数优化基于将空基数 class 的 "storage" 与其他类型别名化。通常,这是通过为它提供与父对象相同的地址 class 来完成的,这意味着该地址通常与第一个非空基或第一个成员子对象相同。
如果编译器为基 class empty_class
提供了与联合成员相同的地址,那么您将拥有 class 的两个不同的子对象(big::empty_class
和big::a
) 具有相同的地址但不同的对象。
这样的布局会违反唯一身份规则。因此,编译器不能在这里使用空基优化。这也是 big
不是标准布局的原因。
union
在这里是一条红鲱鱼。
如果简化为
struct empty{};
struct big : empty
{
empty e;
};
则sizeof(big)
必须大于sizeof(empty)
。这是因为 big
中有两个 empty
类型的对象,因此它们需要不同的地址。 空基优化不能在这里应用,因为它可以用于
struct small : empty
{
int n;
};
您可以期望 sizeof(small)
成为 sizeof(int)
。