我是否应该从传递给一个函数的 `new` 中删除指向 `shared_ptr` 的指针?
Should I delete pointer from `new` passed to a function which makes into a `shared_ptr`?
在下面的代码示例中:
#include <iostream>
class Foo{
};
class Bar{
public:
void addFoo(Foo *foo){
auto my_foo = std::shared_ptr<Foo>(foo);
}
};
int main() {
auto bar = Bar();
bar.addFoo(new Foo());
return 0;
}
我是否需要通过 bar.addFoo(new Foo)
调用清理在 main()
中创建的指针,或者这将由创建 shared_ptr
的 Bar
处理它的?我的理解是 auto my_foo = std::shared_ptr<Foo>(foo);
将使用复制构造函数将此指针复制到 my_foo
中,而使原始指针悬空,对吗?
执行此操作的正确 C++ 方法如下:
#include <iostream>
class Foo{
};
class Bar{
public:
void addFoo(Foo foo){
auto my_foo = std::make_shared<Foo>(foo);
}
};
int main() {
auto bar = Bar();
bar.addFoo(Foo());
return 0;
}
这避免了任何原始指针或裸新,并且完全是异常安全的。此外,std::make_shared
引入了一些性能优势。
这里的一件令人困惑的事情是代码似乎不必要地复制了 Foo
对象,但是,自 C++17 以来,由于 Return 值优化 (RVO),您保证没有副本完全没有(将 Foo
作为参数传递给 addFoo
时)。
原始指针不管理生命周期结束,但共享指针可以。当您从原始指针创建共享指针时,共享指针取得对象的所有权。这意味着当指向它的最后一个共享指针超出范围时,该对象将被销毁。
在您的代码中,my_foo
获得使用 new Foo()
创建的对象的所有权,在 addFoo
returns 时超出范围,并且因为它包含唯一的共享引用,正确销毁对象。
采用原始指针的构造函数的真正想法是将所有权传递给 std::shared_ptr
。所以,不,您不必 delete
将原始指针传递给 std::shared_ptr
。这样做会导致双删,也就是UB
请注意,通常传递原始指针是危险的。考虑以下更通用的示例:
void addFoo(Foo *foo){
// some code which could throw an exception
auto my_foo = std::shared_ptr<Foo>(foo);
}
如果在构造my_foo
之前抛出异常,foo
就会泄漏。
如果您没有特殊原因要传递原始指针,请考虑以下备选方案:
class Bar {
public:
template<class... Args>
void addFoo(Args... args){
auto my_foo = std::make_shared<Foo>(args...);
}
};
int main() {
auto bar = Bar();
bar.addFoo();
return 0;
}
此处传递参数(如果有)以在 addFoo()
中构造 Foo
,而不是在调用 addFoo()
.
之前构造 Foo
完美转发args...
如果需要可以使用:
template<class... Args>
void addFoo(Args&&... args){
auto my_foo = std::make_shared<Foo>(std::forward<Args>(args)...);
}
您可以使用 make_shared 创建共享指针。如果你想在 main 中构造 Foo (例如,因为你在那里有可用的参数),那么在构造点使用 make_shared 并传递 shared_ptr on.
#include <iostream>
class Foo{
~Foo() { std::cout << "Foo destructed" << std::endl; }
};
class Bar{
public:
void addFoo(std::shared_ptr<Foo> foo){
auto my_foo = foo;
}
};
int main() {
auto bar = Bar();
bar.addFoo(std::make_shared<Foo>());
return 0;
}
delete 也会调用你的析构函数。您可以通过打印消息来测试共享指针是否破坏了您的对象,或者是否需要删除。
你写的代码是正确的。但是在现代 C++ 中,您不应该使用原始指针 new
和 delete
除非您必须与这样做的代码进行互操作。如果你能帮助它(如果问题评论有任何迹象,你可以),请一直使用智能指针:
#include <iostream>
#include <memory>
class Foo {};
class Bar {
public:
void addFoo(std::unique_ptr<Foo> foo) {
auto my_foo = std::shared_ptr<Foo>(std::move(foo));
}
};
int main() {
auto bar = Bar();
bar.addFoo(std::make_unique<Foo>());
return 0;
}
上面,addFoo
成员函数接收指针作为 unique_ptr
,并使用 std::move
将指针的所有权从 unique_ptr
转移到 shared_ptr
不复制所指对象;在构造 shared_ptr
之后,unique_ptr
处于空状态。您也可以让 addFoo
直接接收 shared_ptr
,或在成员函数内就地构造对象,如 Evg 的回答。
使用 unique_ptr
而不是原始指针清楚地表明该方法打算取得分配的所有权,并鼓励调用者自己使用智能指针,从而降低他们忘记 delete
他们稍后分配。
在下面的代码示例中:
#include <iostream>
class Foo{
};
class Bar{
public:
void addFoo(Foo *foo){
auto my_foo = std::shared_ptr<Foo>(foo);
}
};
int main() {
auto bar = Bar();
bar.addFoo(new Foo());
return 0;
}
我是否需要通过 bar.addFoo(new Foo)
调用清理在 main()
中创建的指针,或者这将由创建 shared_ptr
的 Bar
处理它的?我的理解是 auto my_foo = std::shared_ptr<Foo>(foo);
将使用复制构造函数将此指针复制到 my_foo
中,而使原始指针悬空,对吗?
执行此操作的正确 C++ 方法如下:
#include <iostream>
class Foo{
};
class Bar{
public:
void addFoo(Foo foo){
auto my_foo = std::make_shared<Foo>(foo);
}
};
int main() {
auto bar = Bar();
bar.addFoo(Foo());
return 0;
}
这避免了任何原始指针或裸新,并且完全是异常安全的。此外,std::make_shared
引入了一些性能优势。
这里的一件令人困惑的事情是代码似乎不必要地复制了 Foo
对象,但是,自 C++17 以来,由于 Return 值优化 (RVO),您保证没有副本完全没有(将 Foo
作为参数传递给 addFoo
时)。
原始指针不管理生命周期结束,但共享指针可以。当您从原始指针创建共享指针时,共享指针取得对象的所有权。这意味着当指向它的最后一个共享指针超出范围时,该对象将被销毁。
在您的代码中,my_foo
获得使用 new Foo()
创建的对象的所有权,在 addFoo
returns 时超出范围,并且因为它包含唯一的共享引用,正确销毁对象。
采用原始指针的构造函数的真正想法是将所有权传递给 std::shared_ptr
。所以,不,您不必 delete
将原始指针传递给 std::shared_ptr
。这样做会导致双删,也就是UB
请注意,通常传递原始指针是危险的。考虑以下更通用的示例:
void addFoo(Foo *foo){
// some code which could throw an exception
auto my_foo = std::shared_ptr<Foo>(foo);
}
如果在构造my_foo
之前抛出异常,foo
就会泄漏。
如果您没有特殊原因要传递原始指针,请考虑以下备选方案:
class Bar {
public:
template<class... Args>
void addFoo(Args... args){
auto my_foo = std::make_shared<Foo>(args...);
}
};
int main() {
auto bar = Bar();
bar.addFoo();
return 0;
}
此处传递参数(如果有)以在 addFoo()
中构造 Foo
,而不是在调用 addFoo()
.
Foo
完美转发args...
如果需要可以使用:
template<class... Args>
void addFoo(Args&&... args){
auto my_foo = std::make_shared<Foo>(std::forward<Args>(args)...);
}
您可以使用 make_shared 创建共享指针。如果你想在 main 中构造 Foo (例如,因为你在那里有可用的参数),那么在构造点使用 make_shared 并传递 shared_ptr on.
#include <iostream>
class Foo{
~Foo() { std::cout << "Foo destructed" << std::endl; }
};
class Bar{
public:
void addFoo(std::shared_ptr<Foo> foo){
auto my_foo = foo;
}
};
int main() {
auto bar = Bar();
bar.addFoo(std::make_shared<Foo>());
return 0;
}
delete 也会调用你的析构函数。您可以通过打印消息来测试共享指针是否破坏了您的对象,或者是否需要删除。
你写的代码是正确的。但是在现代 C++ 中,您不应该使用原始指针 new
和 delete
除非您必须与这样做的代码进行互操作。如果你能帮助它(如果问题评论有任何迹象,你可以),请一直使用智能指针:
#include <iostream>
#include <memory>
class Foo {};
class Bar {
public:
void addFoo(std::unique_ptr<Foo> foo) {
auto my_foo = std::shared_ptr<Foo>(std::move(foo));
}
};
int main() {
auto bar = Bar();
bar.addFoo(std::make_unique<Foo>());
return 0;
}
上面,addFoo
成员函数接收指针作为 unique_ptr
,并使用 std::move
将指针的所有权从 unique_ptr
转移到 shared_ptr
不复制所指对象;在构造 shared_ptr
之后,unique_ptr
处于空状态。您也可以让 addFoo
直接接收 shared_ptr
,或在成员函数内就地构造对象,如 Evg 的回答。
使用 unique_ptr
而不是原始指针清楚地表明该方法打算取得分配的所有权,并鼓励调用者自己使用智能指针,从而降低他们忘记 delete
他们稍后分配。