提升(boost)、共享指针(shared ptr)和弱指针(weak ptr)?什么时候使用哪一个?

46

在我的当前项目中,我广泛使用了boost::shared_ptr

最近,我的团队成员也开始使用weak_ptr。我不知道应该在什么情况下选择哪个。

除此之外,如果我想将weak_ptr转换为shared_ptr,我该怎么做?通过对weak_ptr进行锁定以创建shared_ptr会影响到其他线程的代码吗?

4个回答

71
总结一下,强指针保证自身的有效性。例如,在以下情况下使用它们:拥有所指对象;创建和销毁对象;没有定义行为,如果对象不存在;需要强制对象存在。相比之下,弱指针保证了知道自己的有效性。例如,在以下情况下使用它们:访问它,但它不是你的;如果对象不存在,则已定义行为。对于多线程,请像处理其他没有内置线程安全性的事物一样小心处理它们,并注意上述保证在多线程环境下仍然成立。此外,boost共享指针还具有垃圾收集器的功能,因为当最后一个强指针指向另一个位置或消失时,对象将被删除。总之,boost共享指针库可以让您不用犯错误就能编写程序,但它不能替代花时间正确设计指针、对象所有权和生命周期的做法。如果您有这样的设计,可以使用该库执行它。如果您没有这样的设计,则可能会遇到不同于以前的问题。

我喜欢这个问题在面试中一直被提到... :D - Narfanator

23

当你创建的对象包含循环引用时,即使用 shared_ptr 指向一个具有返回指向自身 shared_ptr 的对象时,请使用 weak_ptr。这是因为 shared_ptr 无法处理循环引用 - 当两个对象超出范围时,相互引用意味着它们不会被 "垃圾回收",因此会发生内存泄漏。由于 weak_ptr 不会增加引用计数,因此不会出现循环引用问题。这也意味着一般情况下,如果想要获取指向某个引用计数对象的指针并且不想增加其引用计数,则应使用 weak_ptr

否则,您可以使用 shared_ptr

欲了解更多信息,请查看 Boost 文档


1
那是错误的,甚至荒谬的。当您需要所有权时,请使用一个拥有(强)智能指针。当您希望在对象仍然存在时获取强指针,并且在其已被销毁时什么也不得到时,请使用弱指针。Narfanator对此进行了完美的解释。如果您有循环依赖,则无法使用任何一种指针。 - curiousguy
2
@curiousguy http://www.boost.org/doc/libs/1_47_0/libs/smart_ptr/shared_ptr.htm '使用 weak_ptr 来"打破循环引用"。'因此,当您遇到循环引用时,可以使用弱指针。此外,OP 询问了 shared_ptr 和 weak_ptr,所以理性认为可以忽略 scoped_ptr 的提及。否则,我只是说您可以使用 shared_ptr,而不是必须或者它是您唯一的选择,并且我甚至链接了文档。这有什么“错误”或“荒谬”的呢? - blwy10
4
“@curiousguy那只是语义学的问题。将循环定义消除,即仍然存在指针循环,但循环的一条边是一个weak_ptr引用,这就打破了循环!因此,您已经承认在存在循环时可以使用weak_ptr,只是在您的术语中循环停止存在,这与我所说的话只是措辞不同而已。” - blwy10
1
那么,为什么非得这样定义“引用”这个未经修饰的词呢? - blwy10
4
那么这场辩论的胜者是谁? - stewart99
显示剩余15条评论

7

共享指针实现引用计数,弱指针不影响引用计数;如果您没有对对象拥有共享指针,只有弱指针,则该对象被删除,并且弱指针现在会告诉您对象已经丢失。

使用弱指针有两个原因:

  1. 消除引用计数的增加/减少成本;但是您不应这样做,因为它容易出错,并且并不能真正节省多少时间。
  2. 在簿记数据结构中,例如,您拥有所有“存活”的Foo对象的索引,即在其他地方使用的对象,如果所有“真实”使用已结束,则不想在索引中保留Foo。这是弱指针的基本实际用例。当然也存在其他用例。

因此,总体而言,我的建议是仅在知道要让所引用的对象被删除并希望检测到时才使用弱指针。在其他情况下使用共享指针(引用计数)或直接指针,特别是在方法局部变量中,当您知道对象不会被删除时。虽然也容易出错,但比共享指针更快。

注:循环对象不需要弱指针,在大多数正确构造的程序中可以使用非干扰型常规指针。弱指针风险较小。


@Trent 其实并没有这个,那是一个打错字——一开始是两个,然后我把它编辑成“三个”以适应另一个答案关于循环结构的评论,但我开始想为什么,因为在垃圾回收语言中弱指针不是用来处理循环对象的,然后我意识到你不需要它们,所以我把它删掉了,并添加了N.B. 当然你可以使用它们,但它们只是一种断言类型,你不需要它们来处理循环对象。 - Antti Huima
弱指针确实会影响引用计数——shared_ptr的实现需要为了太过复杂无法在评论中说明的原因,保持单独的强引用计数和弱引用计数。弱指针的作用就是为了打破循环引用,就是这样。 - Terry Mahaffey
1
它变得有点神秘了。但是在垃圾回收良好的语言中,弱指针也存在。弱指针的概念最初与循环无关,而是与检测资源管理器(例如引用计数、垃圾回收等)已删除其后面的对象有关。 - Antti Huima
2
我不想谈宗教信仰,但我认为使用智能指针持有的内存的原始指针是不安全的,请参见我的答案以获取更多信息。 - Rasmus Storjohann
“使用弱指针的原因有两个”这种说法是错误的。你要么需要一个强引用,要么不需要。你不能在弱引用和强引用之间选择,它是由设计约束所决定的。 - curiousguy
@TerryMahaffey "weak_ptr是为了打破循环,就这样" 真是荒谬。要么有循环,要么没有循环。弱引用不是一个魔法棒,可以"打破"循环(无论那意味着什么)。 - curiousguy

-10

如果你不是在尝试实现垃圾回收器,那么最好不要使用弱指针。在C++中实现垃圾回收器并不是一个好主意,因为很难密切跟踪可能出错的所有内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接