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

C++实现菱形shared_ptr结构深拷贝的类型安全方案咨询

类型安全的多态对象深拷贝方案

要实现保留指针拓扑(包括菱形引用、循环引用)的深拷贝,同时避免手动动态类型转换的不安全性,直接用虚克隆接口+类型擦除的拷贝映射两层实现即可,完全适配pybind11绑定的多态对象场景。

核心思路

  • 给所有参与拷贝的多态类型统一继承抽象克隆基类,每个类自己实现递归拷贝逻辑,所有类型转换都在类内部完成,从根源避免外部动态转换的bug
  • 用std::unordered_map<const void*, std::shared_ptr<void>>做全局已拷贝对象索引,key是原对象的裸指针(擦除类型),value是新拷贝的对象,所有递归拷贝过程共享这张表,自动去重,完全不需要感知对象的具体类型。

可直接运行的实现代码

#include <memory>
#include <unordered_map>
#include <iostream>

// 所有可深拷贝类型的公共基类
class Cloneable {
public:
    virtual std::shared_ptr<Cloneable> clone(std::unordered_map<const void*, std::shared_ptr<void>>& copied_map) const = 0;
    virtual ~Cloneable() = default;
};

class C : public Cloneable {
public:
    std::shared_ptr<Cloneable> clone(std::unordered_map<const void*, std::shared_ptr<void>>& copied_map) const override {
        if (copied_map.contains(this)) {
            return std::static_pointer_cast<C>(copied_map[this]);
        }
        auto new_c = std::make_shared<C>();
        copied_map[this] = new_c;
        return new_c;
    }
};

class B : public Cloneable {
public:
    std::shared_ptr<C> c;
    B(std::shared_ptr<C> c):c(c){}

    std::shared_ptr<Cloneable> clone(std::unordered_map<const void*, std::shared_ptr<void>>& copied_map) const override {
        if (copied_map.contains(this)) {
            return std::static_pointer_cast<B>(copied_map[this]);
        }
        // 递归拷贝成员,共享拷贝映射自动处理引用拓扑
        auto new_c = std::static_pointer_cast<C>(c->clone(copied_map));
        auto new_b = std::make_shared<B>(new_c);
        copied_map[this] = new_b;
        return new_b;
    }
};

class A : public Cloneable {
public:
    std::shared_ptr<B> b1;
    std::shared_ptr<B> b2;
    A(std::shared_ptr<B> b1, std::shared_ptr<B> b2):b1(b1), b2(b2){}

    std::shared_ptr<Cloneable> clone(std::unordered_map<const void*, std::shared_ptr<void>>& copied_map) const override {
        if (copied_map.contains(this)) {
            return std::static_pointer_cast<A>(copied_map[this]);
        }
        auto new_b1 = std::static_pointer_cast<B>(b1->clone(copied_map));
        auto new_b2 = std::static_pointer_cast<B>(b2->clone(copied_map));
        auto new_a = std::make_shared<A>(new_b1, new_b2);
        copied_map[this] = new_a;
        return new_a;
    }
};

auto init(){
    auto c = std::make_shared<C>();
    auto b1 = std::make_shared<B>(c);
    auto b2 = std::make_shared<B>(c);
    auto a = std::make_shared<A>(b1,b2);
    return a;
}

int main(){
    auto a = init();
    std::unordered_map<const void*, std::shared_ptr<void>> copied_map;
    auto a2 = std::static_pointer_cast<A>(a->clone(copied_map));

    // 验证菱形结构保留:两个B指向同一个C
    std::cout << "Topology preserved: " << (a2->b1->c.get() == a2->b2->c.get()) << "\n"; // 输出1
    // 验证是深拷贝:新旧C不是同一个对象
    std::cout << "Deep copy valid: " << (a->b1->c.get() != a2->b1->c.get()) << "\n"; // 输出1
    return 0;
}

方案优势

  • 完全类型安全:所有向下转换都在对应类的clone方法内完成,转换目标类型在编译期确定,不会出现跨类型转换的未定义行为
  • 自动保留任意指针拓扑:不管是菱形共享引用还是循环引用,只要所有shared_ptr成员的拷贝都走同一张映射表,就不会出现重复拷贝或空悬指针
  • 适配pybind11工作流:绑定时只需要给所有导出类统一继承Cloneable基类即可,不需要在Python侧写额外逻辑,C++侧单次调用即可完成整个对象图的拷贝,满足并行仿真多随机种子运行的需求。

如果不想手动给每个类编写clone方法,可以结合pybind11的类型注册信息,写一个通用模板自动生成clone逻辑,遍历类注册的所有shared_ptr类型成员递归调用clone即可,不需要手动为每个类写重复代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 11:48:17