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

如何为C++ union定义拷贝构造函数与赋值运算符?

实现Union的拷贝构造与赋值运算符

首先要明确:C++的union不会自动跟踪当前活跃的成员,而你的apple和pear都包含string(非平凡类型),直接拷贝会导致未定义行为——因为你不知道要拷贝哪个成员,也无法正确析构旧成员。

解决办法是给union加一个"类型标记",用来记录当前存储的是apple还是pear,再基于这个标记实现拷贝逻辑。

完整实现代码

#include <string>
using namespace std;

struct apple {
    string origin;
};

struct pear {
    string origin;
};

union fruit {
    // 类型标记:记录当前活跃的成员
    enum class Type { Apple, Pear } type;

    // 成员
    apple apple;
    pear pear;

    // 默认构造:初始化type并显式构造apple
    fruit() : type(Type::Apple) {
        new(&apple) apple();
    }

    // 专用构造:构造apple
    fruit(const apple& a) : type(Type::Apple) {
        new(&apple) apple(a);
    }

    // 专用构造:构造pear
    fruit(const pear& p) : type(Type::Pear) {
        new(&pear) pear(p);
    }

    // 拷贝构造函数
    fruit(const fruit& other) : type(other.type) {
        switch (other.type) {
            case Type::Apple:
                // placement new:在当前apple内存位置构造拷贝
                new(&apple) apple(other.apple);
                break;
            case Type::Pear:
                new(&pear) pear(other.pear);
                break;
        }
    }

    // 赋值运算符
    fruit& operator=(const fruit& rhs) {
        // 处理自赋值,避免析构后拷贝已失效的对象
        if (this == &rhs) return *this;

        // 先析构当前活跃的成员
        switch (type) {
            case Type::Apple:
                apple.~apple();
                break;
            case Type::Pear:
                pear.~pear();
                break;
        }

        // 拷贝rhs的成员并更新类型标记
        type = rhs.type;
        switch (rhs.type) {
            case Type::Apple:
                new(&apple) apple(rhs.apple);
                break;
            case Type::Pear:
                new(&pear) pear(rhs.pear);
                break;
        }

        return *this;
    }

    // 析构函数:根据类型标记析构对应成员
    ~fruit() {
        switch (type) {
            case Type::Apple:
                apple.~apple();
                break;
            case Type::Pear:
                pear.~pear();
                break;
        }
    }
};

关键细节解释

  • 类型标记type:这是核心,必须用它跟踪当前哪个成员处于活跃状态,否则无法安全完成拷贝、赋值或析构操作。
  • placement new:union不会自动构造非平凡类型的成员,所以需要用new(&成员地址) 类型(参数)的方式,在已分配的内存位置显式构造对象。
  • 显式析构调用:同样,union不会自动调用非平凡类型的析构函数,所以在union析构、赋值前,都要根据类型标记显式调用对应成员的析构函数,避免内存泄漏。
  • 自赋值检查:赋值运算符里必须先判断是否自赋值,否则会先析构自身,再去拷贝已经失效的对象,引发未定义行为。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 03:01:00