我知道,首选规则是始终使用shared_ptr
S或其他智能指针。但是,我不确定如何在没有std::make_shared
复制CTOR的情况下确切实施它。我会详细说明:
问题
我想创建一个类似树的表达,例如:
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 = x+y
中删除z
时,我们都会在x
和y
中调用delete
,这可以在堆栈上分配。,我们绝对不想在堆栈上进行delete
。
那么在这种情况下我该怎么办?
让我知道我是否应该在上下文上提供更多信息。
通过智能指针接受。正确应用智能指针的概念意味着您必须考虑数据的所有权语义,而不是自动调用新/删除。
您接受对非const对象的引用。这并不意味着共享,仅修改对象。正如您正确指出的那样,只需为他们创建明智的指针就拼写灾难。
add
的呼叫者(即使只是您)必须知道他们传递的数据即将共享。他们知道自己是否通过智能指针自己的唯一途径。因此,您应该将add
更改为:
foo add(std::shared_ptr<foo> f1, std::shared_ptr<foo> f2){
return foo{f1, f2};
}
现在, add
的用户唯一有意炸毁了这一点。但是他们很可能不会。此外,现在表示用户在调用代码方面具有更大的灵活性。他们可以为通过的每个对象控制分配器和eleter。