C++:深度复制菱形指针结构



在我的模拟软件中,我使用pybind11生成对象。因此,在编译时,所有对象都存储在std::shared_ptr中,其结构未知。为了实现模拟的并行化,我需要用不同的种子运行相同的配置。我想在C++端的一个调用中实现这些对象的复制。

以下是一个最小的例子,其中我希望a2a的深度复制,具有菱形结构。

// Type your code here, or load an example.
#include <memory>
#include <map>
#include <iostream>
class C{};
class B{
public:
B(std::shared_ptr<C> c):c(c){}
std::shared_ptr<C> c;
};
class A{
public:
A(std::shared_ptr<B> b1, std::shared_ptr<B> b2):b1(b1), b2(b2){}
std::shared_ptr<B> b1;
std::shared_ptr<B> b2;
};
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();
auto a2 = a; //deepcopy of a, where b1 and b2 of the copy point to the same object C
}

我想到的唯一解决方案是传递一张地图<指针,shared_ptr>。这允许在shared_ptr已经被深度复制的情况下进行查找。(在这里,我在打字方面遇到了一些问题,因为我需要动态地回滚类型。这感觉真的很难看,而且很容易出错。)

您可以使用std::shared_ptr<void>来键入擦除所有共享指针,使用std::static_pointer_cast来往返于实际类型。

using Seen = std::set<std::shared_ptr<void>>;
template <typename T>
std::shared_ptr<T> deep_copy(std::shared_ptr<T> source, Seen & seen) {
if (auto it = seen.find(std::static_pointer_cast<void>(source)); it != seen.end()) {
return std::static_pointer_cast<T>(*it);
}
auto dest = make(*source, seen);
seen.insert(std::static_pointer_cast<void>(dest));
return dest;
}

然后,您可以编写采用现有实例和seen映射的构造函数来深度复制成员,从而允许它们是私有的。

template <typename T>
std::shared_ptr<T> make(const T & source, Seen & seen) {
return std::make_shared<T>(source, seen);
}    
class C{
public:
C(){}
C(const C &, Seen &){}    
};    
class B{
std::shared_ptr<C> c;
public:
B(std::shared_ptr<C> c):c(c){}
B(const B & other, Seen & seen):c(deep_copy(other.c, seen)){}
};
class A{
std::shared_ptr<B> b1;
std::shared_ptr<B> b2;
public:
A(std::shared_ptr<B> b1, std::shared_ptr<B> b2):b1(b1), b2(b2){}
A(const A & other, Seen & seen):b1(deep_copy(other.b1, seen)), b2(deep_copy(other.b2, seen)){}
};
int main(){
auto a = init();
Seen a2_seen;
auto a2 = deep_copy(a, a2_seen);
}

或者,对于每种类型都可以有make的重载,如果成员是私有的,则make<T>需要由T加好友。

std::shared_ptr<C> make(const C &, Seen &) {
return std::make_shared<C>();
}
std::shared_ptr<B> make(const B & other, Seen & seen) {
auto c = deep_copy(other.c, seen);
return std::make_shared<B>(c);
}
std::shared_ptr<A> make(const A & other, Seen & seen) {
auto b1 = deep_copy(other.b1, seen);
auto b2 = deep_copy(other.b2, seen);
return std::make_shared<A>(b1, b2);
}