我使用哪种指针?
好的,所以最后一次我写C ++为生, std::auto_ptr
是所有的std lib都可用的,而boost::shared_ptr
是所有的愤怒。 我从来没有真正看过其他提供的智能指针types提升。 我明白,C ++ 11现在提供了一些types的提升,但不是全部。
那么有人有一个简单的algorithm来确定何时使用哪个智能指针? 优选地包括关于哑指针(诸如T*
原始指针)和其余的升压智能指针的build议。 (这样的事情会很棒)。
共享所有权:
采用的标准shared_ptr
和weak_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_ptr
是boost::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_ptr
对T[]
有一个特殊的自动调用结果的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_ptr
和shared_ptr
再次成为赢家。
作用域指针:与unique_ptr
无关,就像auto_ptr
一样。
没有什么比这更多的了。 在没有移动语义的C ++ 03中,这种情况非常复杂,但在C ++ 11中,build议非常简单。
其他智能指针仍然有用,如intrusive_ptr
或interprocess_ptr
。 但是,在一般情况下,它们非常小众,完全没有必要。
何时使用unique_ptr的情况:
- 工厂方法
- 指针的成员(包括pimpl)
- 在stl中保存指针(避免移动)
- 使用大型本地dynamic对象
什么时候使用shared_ptr的情况:
- 跨线程共享对象
- 绑定或捕获指针(使用lambda或std :: bind)
- 一般共享对象
- 自定义删除者
什么时候使用weak_ptr的例子:
- 充当通用参考的大型地图(来自所有开放式套接字的地图)
随意编辑和添加更多