我使用哪种指针?

好的,所以最后一次我写C ++为生, std::auto_ptr是所有的std lib都可用的,而boost::shared_ptr是所有的愤怒。 我从来没有真正看过其他提供的智能指针types提升。 我明白,C ++ 11现在提供了一些types的提升,但不是全部。

那么有人有一个简单的algorithm来确定何时使用哪个智能指针? 优选地包括关于哑指针(诸如T*原始指针)和其余的升压智能指针的build议。 (这样的事情会很棒)。

共享所有权:
采用的标准shared_ptrweak_ptr与Boost相同。 当你需要共享一个资源,并不知道哪一个是最后一个活着的时候使用它们。 使用weak_ptr观察共享资源而不影响其生存期,不要中断周期。 shared_ptr循环通常不会发生 – 两个资源不能彼此拥有。

请注意,Boost还提供了shared_array ,它可能是shared_ptr<std::vector<T> const>一个合适的替代scheme。

接下来,Boost提供了intrusive_ptr ,如果您的资源已经提供引用计数pipe理,并且希望将其采用到RAII原则,那么这是一个轻量级的解决scheme。 这个标准没有被采纳。

独特所有权:
Boost也有一个scoped_ptr ,它不可复制,你不能指定一个删除器。 std::unique_ptrboost::scoped_ptr在类boost::scoped_ptr上, 当你需要一个智能指针时 ,它应该是你的默认select 。 它允许你在模板参数中指定一个删除器,并且可移动 ,与boost::scoped_ptr不同。 它也可以在STL容器中完全使用,只要你不使用需要可复制types的操作(显然)。

再次注意,Boost有一个数组版本: scoped_array ,这个标准通过要求std::unique_ptr<T[]>部分专门化来完成,它将delete[]指针而不是delete指针(使用default_delete r)。 std::unique_ptr<T[]>也提供了operator[]而不是operator*operator->

请注意, std::auto_ptr仍然在标准中,但不推荐使用§D.10 [depr.auto.ptr]

类模板auto_ptr已弃用。 [ 注意:类模板unique_ptr (20.7.1)提供了一个更好的解决scheme。 – 注意 ]

没有所有权:
使用愚蠢的指针(原始指针)或引用非拥有引用资源,当你知道资源将超过引用的对象/范围。 当您需要可空性或可重置性时,首选参考并使用原始指针。

如果你想要一个对资源的非拥有引用,但是你不知道资源是否会超过引用它的对象,那么把资源打包到一个shared_ptr并使用一个weak_ptr – 你可以testing父类shared_ptr是否活着lock ,如果资源仍然存在,将返回非null的shared_ptr 。 如果要testing资源是否死亡,请使用expired 。 这两者可能听起来很相似,但是在并发执行方面却非常不同,因为expired只能保证它对于单个语句的返回值。 像一个看似无辜的testing

 if(!wptr.expired()) something_assuming_the_resource_is_still_alive(); 

是一个潜在的竞争条件。

决定使用什么智能指针是一个所有权问题。 在资源pipe理方面,如果对象B控制对象B的生命周期,则对象A 拥有对象B.例如,成员variables由其各自的对象拥有,因为成员variables的生命周期与对象的生命周期相关联。 您可以根据对象的拥有方式来select智能指针。

请注意,软件系统中的所有权与软件之外的所有权是分开的。 例如,一个人可能“拥有”他们的家,但这并不意味着一个Person对象可以控制一个House对象的生命周期。 将这些真实的世界概念与软件概念相结合,是将自己编入漏洞的绝对方法。


如果您拥有该对象的唯一所有权,请使用std::unique_ptr<T>

如果您有共享对象的所有权…
– 如果所有权中没有循环,则使用std::shared_ptr<T>
– 如果有循环,则定义一个“方向”,并在一个方向上使用std::shared_ptr<T>在另一个方向上使用std::weak_ptr<T>

如果对象拥有你,但有可能没有拥有者,则使用普通指针T* (例如父指针)。

如果对象拥有你(或者另有保证),请使用引用T&


注意:请注意智能指针的成本。 在内存或性能有限的环境中,只需使用普通的指针来pipe理内存就可以了。

花费:

  • 如果你有一个自定义的删除(例如,你使用分配池),那么这将会导致每个指针开销,可以很容易地通过手动删除避免。
  • std::shared_ptr具有复制引用计数增量的开销,加上销毁后的减量,随后是删除所保存对象的0计数检查。 根据实现,这可能会膨胀您的代码并导致性能问题。
  • 编译时间。 和所有的模板一样,智能指针对编译时间有负面影响。

例子:

 struct BinaryTree { Tree* m_parent; std::unique_ptr<BinaryTree> m_children[2]; // or use std::array... }; 

二叉树不拥有它的父亲,但树的存在意味着父亲的存在(或nullptr ),所以使用一个正常的指针。 一个二元树(具有值语义)拥有其子级的唯一所有权,所以这些是std::unique_ptr

 struct ListNode { std::shared_ptr<ListNode> m_next; std::weak_ptr<ListNode> m_prev; }; 

在这里,列表节点拥有它的下一个和之前的列表,所以我们定义一个方向,并使用shared_ptr为next和weak_ptr为prev来打破这个循环。

除非需要引用计数,否则请使用unique_ptr<T> ,在这种情况下,请使用shared_ptr<T> (极less数情况下,使用weak_ptr<T>来防止引用循环)。 在几乎所有情况下,可转让的独特的所有权就好了。

生指针:只有当你需要协变的回报时,才有可能发生的非指向。 否则它们不是非常有用的。

数组指针: unique_ptrT[]有一个特殊的自动调用结果的delete[] ,所以你可以安全地做unique_ptr<int[]> p(new int[42]); 例如。 shared_ptr你仍然需要一个自定义的删除器,但是你不需要一个专门的共享或唯一的数组指针。 当然,这样的东西通常最好由std::vector代替。 不幸的是, shared_ptr不提供数组访问函数,所以你仍然需要手动调用get() ,但是unique_ptr<T[]>提供了operator[]而不是operator*operator-> 。 无论如何,你必须检查自己。 这使得shared_ptr用户友好度稍微降低了一点,尽pipe可以说generics优点并没有提升依赖性,使得unique_ptrshared_ptr再次成为赢家。

作用域指针:与unique_ptr无关,就像auto_ptr一样。

没有什么比这更多的了。 在没有移动语义的C ++ 03中,这种情况非常复杂,但在C ++ 11中,build议非常简单。

其他智能指针仍然有用,如intrusive_ptrinterprocess_ptr 。 但是,在一般情况下,它们非常小众,完全没有必要。

何时使用unique_ptr的情况:

  • 工厂方法
  • 指针的成员(包括pimpl)
  • 在stl中保存指针(避免移动)
  • 使用大型本地dynamic对象

什么时候使用shared_ptr的情况:

  • 跨线程共享对象
  • 绑定或捕获指针(使用lambda或std :: bind)
  • 一般共享对象
  • 自定义删除者

什么时候使用weak_ptr的例子:

  • 充当通用参考的大型地图(来自所有开放式套接字的地图)

随意编辑和添加更多