好吧,上次我以c++谋生的时候,std::auto_ptr是所有可用的std库,而boost::shared_ptr是最流行的。我从未真正研究过boost提供的其他智能指针类型。我知道c++ 11现在提供了boost提出的一些类型,但不是全部。
std::auto_ptr
boost::shared_ptr
那么,有人有一个简单的算法来决定何时使用哪个智能指针吗?最好包括关于哑指针(如T*的原始指针)和其他boost智能指针的建议。(像这这样的东西会很好)。
T*
除非需要引用计数,否则一直使用unique_ptr<T>,在这种情况下使用shared_ptr<T>(在极少数情况下,使用weak_ptr<T>来防止引用循环)。在几乎所有情况下,可转让的唯一所有权都是可以的。
unique_ptr<T>
shared_ptr<T>
weak_ptr<T>
原始指针:只有当你需要协变返回时才有用,非拥有的指针可能会发生。除此之外,它们并没有太大用处。
数组指针:unique_ptr有一个针对T[]的专门化,它会自动对结果调用delete[],所以你可以安全地执行unique_ptr<int[]> p(new int[42]);。shared_ptr你仍然需要一个自定义删除器,但你不需要一个专门的共享或唯一数组指针。当然,这样的东西通常最好用std::vector代替。不幸的是,shared_ptr没有提供数组访问函数,所以你仍然需要手动调用get(),但是unique_ptr<T[]>提供了operator[]而不是T[]0和T[]1。在任何情况下,你都必须检查自己的边界。这使得shared_ptr的用户友好性稍差,尽管可以说通用优势和不依赖Boost使得unique_ptr和shared_ptr再次成为赢家。
unique_ptr
T[]
delete[]
unique_ptr<int[]> p(new int[42]);
shared_ptr
std::vector
get()
unique_ptr<T[]>
operator[]
作用域指针:通过unique_ptr使其不相关,就像auto_ptr一样。
auto_ptr
真的没有别的了。在没有移动语义的c++ 03中,这种情况非常复杂,但在c++ 11中,建议非常简单。
还有其他智能指针的用途,如intrusive_ptr或interprocess_ptr。然而,它们是非常小众的,在一般情况下完全不必要。
intrusive_ptr
interprocess_ptr
决定使用哪种智能指针是所有权的问题。当涉及到资源管理时,如果对象A控制对象B的生命周期,则对象A 拥有对象B。例如,成员变量由它们各自的对象拥有,因为成员变量的生命周期与对象的生命周期捆绑在一起。根据对象的归属方式选择智能指针。
请注意,软件系统中的所有权与所有权是分开的,因为我们会在软件之外考虑它。例如,一个人可能“拥有”他们的房子,但这并不一定意味着Person对象可以控制House对象的生命周期。将这些现实世界的概念与软件概念混为一谈,肯定会让你自己陷入困境。
Person
House
如果你拥有对象的唯一所有权,使用std::unique_ptr<T>。
std::unique_ptr<T>
如果你拥有该对象的共享所有权 —如果所有权中没有循环,使用std::shared_ptr<T>. -如果存在循环,定义一个“方向”,并在一个方向上使用std::shared_ptr<T>,在另一个方向上使用std::weak_ptr<T>
std::shared_ptr<T>
std::weak_ptr<T>
如果对象拥有你,但有可能没有所有者,使用普通指针T*(例如父指针)。
如果对象拥有你(或以其他方式保证存在),则使用引用T&。
T&
警告:要注意智能指针的成本。在内存或性能有限的环境中,使用普通指针和更手动的方案来管理内存可能会更有好处。
成本:
std::shared_ptr
例子:
struct BinaryTree { Tree* m_parent; std::unique_ptr<BinaryTree> m_children[2]; // or use std::array... };
二叉树不拥有它的父树,但树的存在意味着它的父树的存在(或nullptr作为根),因此使用普通指针。二叉树(具有值语义)拥有它的子树的唯一所有权,所以它们是std::unique_ptr。
nullptr
std::unique_ptr
struct ListNode { std::shared_ptr<ListNode> m_next; std::weak_ptr<ListNode> m_prev; };
在这里,列表节点拥有它的next和previous列表,所以我们定义了一个方向,并使用shared_ptr表示next,使用weak_ptr表示prev来打破循环。
weak_ptr
注意Boost还提供了shared_array,这可能是shared_ptr<std::vector<T> const>的合适替代品。
shared_array
shared_ptr<std::vector<T> const>
接下来,Boost提供了intrusive_ptr,这是一个轻量级的解决方案,如果你的资源已经提供了引用计数管理,并且你想采用它来实现RAII原则。这个没有被标准采用。
scoped_ptr
boost::scoped_ptr
再次注意,Boost有一个数组版本:scoped_array,标准通过要求std::unique_ptr<T[]>部分特化来统一该版本,该特化将delete[]指针而不是deleteing指针(使用default_deleter)。std::unique_ptr<T[]>也提供了operator[]而不是operator*和operator->。
scoped_array
std::unique_ptr<T[]>
delete
default_delete
operator*
operator->
§D.10 [depr.auto.ptr]
类模板auto_ptr已弃用。类模板unique_ptr(20.7.1)提供了一个更好的解决方案。端注]
如果你想要一个对资源的非所有者引用,但你不知道资源是否会比引用它的对象活得更久,将资源打包在shared_ptr中并使用weak_ptr -你可以用lock测试父shared_ptr是否活,如果资源仍然存在,它将返回一个非空的shared_ptr。如果想测试资源是否已死,请使用expired。这两者听起来很相似,但在并发执行时却非常不同,因为expired只保证了该语句的返回值。一个看似无辜的测试
lock
expired
if(!wptr.expired()) something_assuming_the_resource_is_still_alive();
是潜在的竞争条件。
何时使用unique_ptr的情况:
何时使用shared_ptr的情况:
何时使用weak_ptr的情况:
请随意编辑和添加更多内容