在这种情况下我应该使用智能指针吗?
Should I use smart pointers in this situation?
我知道首选规则是始终使用 shared_ptr
或其他智能指针。但是,我不确定如何在不使用 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 = x+y
中的 z
时,我们都会在 x
和 y
中调用 delete
,它们可以分配在堆。 而且我们绝对不想delete
栈上的东西。
那么遇到这种情况我该怎么办呢?
如果我应该提供更多关于上下文的信息,请告诉我。
通过智能指针接受。正确应用智能指针的概念意味着您必须考虑数据的所有权语义,而不是自动调用 new/delete。
您接受了对非常量对象的引用。这并不意味着共享,仅意味着对象的修改。正如您正确指出的那样,仅创建指向它们的智能指针就会带来灾难。
add
的调用者(即使只是您)必须知道他们传递的数据将被共享。他们知道的唯一方法是他们自己是否传递了智能指针。所以你应该把add
改成这样:
foo add(std::shared_ptr<foo> f1, std::shared_ptr<foo> f2){
return foo{f1, f2};
}
现在,add
的用户唯一能炸毁它的方法就是故意。但他们很可能不会。此外,现在所说的用户在调用您的代码时具有更大的灵活性。他们可以控制他们传递的每个对象的分配器和删除器。
我知道首选规则是始终使用 shared_ptr
或其他智能指针。但是,我不确定如何在不使用 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 = x+y
中的 z
时,我们都会在 x
和 y
中调用 delete
,它们可以分配在堆。 而且我们绝对不想delete
栈上的东西。
那么遇到这种情况我该怎么办呢?
如果我应该提供更多关于上下文的信息,请告诉我。
通过智能指针接受。正确应用智能指针的概念意味着您必须考虑数据的所有权语义,而不是自动调用 new/delete。
您接受了对非常量对象的引用。这并不意味着共享,仅意味着对象的修改。正如您正确指出的那样,仅创建指向它们的智能指针就会带来灾难。
add
的调用者(即使只是您)必须知道他们传递的数据将被共享。他们知道的唯一方法是他们自己是否传递了智能指针。所以你应该把add
改成这样:
foo add(std::shared_ptr<foo> f1, std::shared_ptr<foo> f2){
return foo{f1, f2};
}
现在,add
的用户唯一能炸毁它的方法就是故意。但他们很可能不会。此外,现在所说的用户在调用您的代码时具有更大的灵活性。他们可以控制他们传递的每个对象的分配器和删除器。