我知道首选规则是始终使用shared_ptr
s或其他智能指针。但是,如果没有std::make_shared
的副本,我不确定如何实现它。我将详细说明:
我想创建一个类似树的表达式,如:
struct foo{
std::vector<foo*> v;
};
这允许我执行以下操作:
foo add(foo& f1, foo& f2){
return foo {&f1, &f2};
}
这很好,因为那时两人将成为新节点的孩子。
我希望正确评估以下内容:
foo x, y;
auto z = add(x,y);
assert(z.v[0] == &x && z.v[1] == &y);
这样,没有复制,这一切都很好。但是,我们在这里遇到了资源管理问题。
如果这些元素在堆上分配,并且有人在不知不觉中,那么我们就会遇到内存泄漏。因此,最好的方法是使用智能指针进行RAII:
struct foo{
std::vector<std::shared_ptr<foo> > v;
};
foo add(foo& f1, foo& f2){
return foo {std::make_shared<foo>(f1), std::make_shared<foo>(f2)};
}
这很好,但我们在这里调用f1和f2的副本。假设复制需要很长时间,我们不想承担这笔费用。
我们绝对不能这样做:
foo add(foo& f1, foo& f2){
return foo {std::shared_ptr<foo>(&f1), std::shared_ptr<foo>(&f2)};
}
因为每当我们移除z
中的z = x+y
时,我们都会在delete
和x
上分别调用y
,这可以在堆栈上分配。 我们绝对不希望堆叠中的delete
内容。
那么在这种情况下该怎么做?
如果我应该提供有关背景的更多信息,请告诉我。
答案 0 :(得分:3)
通过智能指针接受。正确应用智能指针的概念意味着您必须考虑数据的所有权语义,而不是自动调用new / delete。
您接受对非const对象的引用。这并不意味着共享,只修改对象。正如你所正确指出的那样,只要创建智能指针就会造成灾难。
add
的来电者(即使只是你)必须知道他们传递的数据即将被分享。他们知道的唯一方法是他们自己是否通过智能指针。所以应将add
更改为:
foo add(std::shared_ptr<foo> f1, std::shared_ptr<foo> f2){
return foo{f1, f2};
}
现在,add
的用户可以有效地解决这个问题。但他们很可能不会。此外,现在所述用户在调用代码时具有更大的灵活性。他们可以控制他们传递的每个对象的分配器和删除器。