You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++中继承空类且含Union的派生类为何内存占用更大?

为什么继承空类的含union类内存占用更大?如何避免?

先看你给出的代码和输出:empty_class的大小是1(C标准要求每个对象必须有唯一地址,所以空类会被分配1字节的占位符),small的大小是12(union的大小由最大成员int data[3]决定,12字节),但big的大小却变成了16,比small多了4字节。这个差异的核心原因和空基类优化(EBO)的限制以及C的对象地址规则有关。

问题根源:同类型子对象的地址唯一性要求

C++标准有一个关键规则:两个独立的、同类型的非位域对象,不能拥有相同的内存地址。我们来拆解big类的布局:

  • big继承自empty_class,所以它包含一个基类子对象(类型为empty_class)。
  • big的union成员里又包含一个empty_class a的实例,这是另一个独立的empty_class对象。

如果编译器尝试对big应用空基类优化(让基类子对象和union成员共享内存地址),就会导致两个同类型的独立对象(基类子对象和a)地址相同,直接违反了C++标准。因此编译器不得不放弃EBO,给基类子对象分配独立的空间。

再加上内存对齐的要求:int的对齐边界是4字节,union的大小是12字节(已经是4的倍数)。基类子对象需要至少1字节的空间,但为了满足对齐要求,编译器会在基类子对象和union之间填充3字节,最终总大小就是1+3+12=16字节,这就是你看到的结果。

而small结构体没有继承关系,它的union里的empty_class a只是union的一个成员,不需要和其他独立的empty_class对象共享地址,所以union的大小直接由最大成员int data[3]决定,总大小就是12字节。

如何避免额外内存开销

针对这个问题,有两种可行的解决方法,取决于你使用的C++版本:

方法1:使用不同类型的空类替代(兼容C++11及以上)

既然问题出在同类型子对象的地址冲突,我们可以给union里的空类创建一个类型别名(派生自原空类),让它和基类子对象的类型不同。这样编译器就可以安全地应用空基类优化,因为不同类型的子对象允许共享地址。

修改后的代码示例:

#include <iostream>
class empty_class { };
// 创建一个和empty_class不同的类型,功能完全一致
struct empty_alias : empty_class {};

struct big : public empty_class {
    union {
        int data[3];
        empty_alias a; // 使用别名类型替代原empty_class
    };
};

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

int main() {
    std::cout << sizeof(empty_class) << std::endl; // 输出1
    std::cout << sizeof(big) << std::endl; // 输出12
    std::cout << sizeof(small) << std::endl; // 输出12
}

方法2:使用C++20的[[no_unique_address]]属性

如果你使用C++20或更高版本,可以直接在基类声明上添加[[no_unique_address]]属性。这个属性告诉编译器:该基类子对象不需要强制拥有唯一的内存地址,只要不违反其他核心规则即可。这样编译器就能重新应用空基类优化,消除额外的内存开销。

修改后的代码示例:

#include <iostream>
class empty_class { };

// 给基类添加[[no_unique_address]]属性
struct big : [[no_unique_address]] 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; // 输出1
    std::cout << sizeof(big) << std::endl; // 输出12
    std::cout << sizeof(small) << std::endl; // 输出12
}

内容的提问来源于stack exchange,提问作者Daniel H

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:24:06