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

含shared_ptr的联合体:改写类A构造析构以适配vector及emplace_back

解决方案

核心问题说明

原来的memset初始化方式会直接破坏std::shared_ptr的内部结构(比如引用计数),而且当union包含非平凡类型(比如带自定义构造/析构的std::shared_ptr)时,C++无法自动管理union成员的生命周期,必须手动跟踪活跃成员并控制构造、析构逻辑。

具体修改步骤

1. 添加判别式跟踪活跃成员

在类A中增加一个标记,用来记录当前union中哪个成员处于活跃状态(同一时间union只能有一个成员有效)。

2. 重写默认构造函数

放弃memset,显式构造默认激活的成员(比如默认激活part1,对齐原来memset后b=0的效果)。

3. 实现析构函数

根据判别式,手动调用活跃成员的析构函数(std::shared_ptr必须手动析构才能正确释放资源)。

4. 补充拷贝/移动语义(可选但必要)

要存入vector,类A必须支持拷贝或移动操作,否则vector扩容时会触发默认拷贝,破坏shared_ptr的引用计数。

修改后的完整代码

#include <memory>
#include <vector>

struct B {
    int b;
};

struct D {
    std::shared_ptr<int> d = nullptr;
};

class A {
public:
    // 标记当前活跃的union成员
    enum ActiveMember { PART1, PART3 };
    ActiveMember active;

    union {
        B part1;
        D part3;
    } parts;

    // 默认构造:激活part1并初始化b为0
    A() : active(PART1) {
        // 用placement new显式构造B成员(平凡类型也可直接赋值,此写法更规范)
        new (&parts.part1) B{0};
    }

    // 析构:根据活跃成员调用对应析构函数
    ~A() {
        if (active == PART3) {
            // 手动调用D的析构,释放shared_ptr资源
            parts.part3.~D();
        }
        // B是平凡类型,无需手动析构
    }

    // 拷贝构造函数
    A(const A& other) : active(other.active) {
        if (active == PART1) {
            new (&parts.part1) B(other.parts.part1);
        } else {
            new (&parts.part3) D(other.parts.part3);
        }
    }

    // 拷贝赋值运算符
    A& operator=(const A& other) {
        if (this != &other) {
            // 先析构当前活跃成员
            if (active == PART3) {
                parts.part3.~D();
            }
            // 构造新的活跃成员
            active = other.active;
            if (active == PART1) {
                new (&parts.part1) B(other.parts.part1);
            } else {
                new (&parts.part3) D(other.parts.part3);
            }
        }
        return *this;
    }

    // 移动构造函数
    A(A&& other) noexcept : active(other.active) {
        if (active == PART1) {
            new (&parts.part1) B(std::move(other.parts.part1));
        } else {
            new (&parts.part3) D(std::move(other.parts.part3));
        }
        // 重置原对象状态,避免重复析构
        other.active = PART1;
    }

    // 移动赋值运算符
    A& operator=(A&& other) noexcept {
        if (this != &other) {
            // 先析构当前活跃成员
            if (active == PART3) {
                parts.part3.~D();
            }
            // 移动构造新成员
            active = other.active;
            if (active == PART1) {
                new (&parts.part1) B(std::move(other.parts.part1));
            } else {
                new (&parts.part3) D(std::move(other.parts.part3));
            }
            // 重置原对象状态
            other.active = PART1;
        }
        return *this;
    }
};

// 使用示例
int main() {
    std::vector<A> vec;
    vec.emplace_back(); // 可正常调用默认构造函数
    return 0;
}

关键注意点

  • 判别式不可少:C++编译器无法自动跟踪union的活跃成员,必须手动标记,否则析构时会导致资源泄漏或非法访问。
  • 绝对不能用memset:memset会直接覆盖std::shared_ptr的内部引用计数等数据,引发严重错误。
  • 显式构造/析构:对于union中的非平凡类型,必须用placement new显式构造,析构时也要手动调用析构函数。

内容的提问来源于stack exchange,提问作者annaa-ka

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 16:06:43