如何为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
相关产品推荐
相关产品推荐

