在以下代码示例中:
#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
中,使原始指针悬空,对吗?
构造函数获取原始指针的想法就是将所有权传递给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)...);
}
您编写的代码是正确的。但在现代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
分配。
原始指针不管理寿命终止,但共享指针管理寿命终止。当您从原始指针创建共享指针时,共享指针将获得对象的所有权。这意味着,当指向该对象的最后一个共享指针超出范围时,该对象将被销毁。
在您的代码中,my_foo
拥有使用new Foo()
创建的对象的所有权,在addFoo
返回时超出范围,并且由于它包含唯一的共享引用,因此正确地销毁了该对象。
正确的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 Value Optimization(RVO(,您可以保证根本没有副本(当将Foo
作为参数传递给addFoo
时(。
您可以使用make_shared创建共享指针。如果你想在main中构造Foo(例如,因为那里有可用的参数(,那么在构造时使用make_shared并传递shared_ptr。
#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还会调用析构函数。您可以通过打印消息来测试共享指针是否会破坏对象,或者是否需要删除。