2011-04-15 101 views
51

我了解如何使用weak_ptrshared_ptr。通过计算其对象中引用的数量,我明白shared_ptr的工作原理。 weak_ptr如何工作?我尝试阅读boost源代码,并且我不太熟悉提升以了解它使用的所有内容。weak_ptr是如何工作的?

谢谢。

+6

另请参见:[共享指针如何工作?](http://stackoverflow.com/questions/2802953/how-do-shared-pointers-work) – 2011-04-15 00:48:35

+0

另请参见[shared_ptr implementation notes](http:// en.cppreference.com/w/cpp/memory/shared_ptr#Implementation_notes) – ks1322 2014-11-23 13:37:01

回答

85

shared_ptr使用额外的“计数器”对象(又称“共享计数”或“控制块”)来存储引用计数。 (顺便说一句:即“计数器”对象也存储删除器。)

shared_ptr每个和weak_ptr包含一个指向实际指针对象,和一个第二指针的“计数器”的对象。

要实现weak_ptr,在“反”对象存储两个不同的计数器:

  • 的“使用计数”是shared_ptr实例指向的对象的数量。
  • “弱计数”是weak_ptr实例指向的对象,加一的数量如果“使用计数”仍然是> 0

当“使用计数”达到零时,指针对象被删除。

当“弱计数”达到零(这意味着“使用计数”也必须为零,见上文)时,“counter”辅助对象被删除。

当您尝试从weak_ptr获取shared_ptr时,该库会自动检查“使用计数”,并且如果其大于0,则会将其增加。如果成功,你得到你的shared_ptr。如果“使用计数”已为零,您将获得一个空的shared_ptr实例。


编辑:现在,为什么他们增加一个弱计数,而不仅仅是释放“反”的对象时,这两个方面降为零?好问题。

另一种方法是在“使用计数”和“弱计数”均为零时删除“计数器”对象。这是第一个原因:在每个平台上检查两个(指针大小的)计数器是不可能的,即使它在哪里,它也比检查一个计数器更复杂。

另一个原因是删除程序必须保持有效,直到它完成执行。由于删除者存储在“计数器”对象中,这意味着“计数器”对象必须保持有效。考虑如果一个对象有一个shared_ptr和一个weak_ptr会发生什么,并且它们在并发线程中同时被重置。我们假设shared_ptr排在第一位。它将“使用次数”减少到零,并开始执行删除程序。现在weak_ptr将“弱计数”减小到零,并且发现“使用计数”也是零。所以它删除了“counter”对象,并删除了它。删除者仍在运行。

当然,会有不同的方法来确保“counter”对象保持活着,但我认为将“弱数”增加一个是非常优雅和直观的解决方案。 “弱计数”成为“计数器”对象的参考计数。而且由于shared_ptr也引用了反制对象,它们也必须增加“弱数”。

可能更直观的解决方案是增加每个单独的shared_ptr的“弱计数”,因为每个单独的shared_ptr都保持对“计数器”对象的引用。

为所有shared_ptr实例添加一个只是一个优化(复制/分配shared_ptr实例时保存一个原子增量/减量)。

+0

它们不检查弱计数和使用计数是否为零,然后删除计数器对象。但是,如果使用计数不为零,他们会将弱计数增加1,然后只检查弱计数。这是为什么? – 2011-04-15 09:53:11

+0

由于这只是一个有点难以解释的评论,我更新了我的答案。 – 2011-04-15 10:54:52

+0

也许他们只为每个shared_ptr增加1而不是1的原因是,那么当他们需要触摸它来调用删除器时,他们只需触摸一次计数器对象,而不是每次减少使用计数器? – 2011-04-15 12:20:53

-5

基本上,“weak_ptr”是一个普通的“T *”指针,它可以让你在代码中稍后恢复一个强引用,即“shared_ptr”。

就像普通的T *一样,weak_ptr不做任何引用计数。在内部,为了支持任意类型T的引用计数,STL(或任何其他实现这种逻辑的库)创建一个封装器对象,我们称之为“锚定器”。 “锚点”仅用于实现我们需要的引用计数和“计数为零时,呼叫删除”行为。

在一个强大的参考中,shared_ptr实现了它的副本,操作符=,构造函数,析构函数和其他相关的API来更新“Anchor”的引用计数。这就是shared_ptr如何确保您的“T”与使用它的人一样长寿。在“weak_ptr”中,这些相同的API只是复制实际的Anchor ptr。他们不更新引用计数。

这就是为什么“weak_ptr”最重要的API是“过期”以及名字不太好的“锁”。 “过期”告诉你底层对象是否仍然在 - 即“它是否已经删除了,因为所有强引用都超出了范围?”。 “Lock”将(如果可以)将weak_ptr转换为强参考shared_ptr,恢复引用计数。

顺便说一句,“锁定”是该API的一个可怕的名字。你不是(只是)调用一个互斥体,而是用一个弱的参考来创建一个强有力的参考,并用这个“锚”来表现。这两个模板中最大的缺陷是它们没有实现operator->,所以要对对象做任何事情都必须恢复原始的“T *”。他们主要是为了支持诸如“shared_ptr”之类的东西,因为原始类型不支持“ - >”运算符。

+4

你写道:*在“weak_ptr”中,那些相同的API只是复制实际的Anchor ptr。他们不更新引用计数。*这是不正确的;他们更新* weak *引用计数,它管理锚本身的生命周期。否则,你会如何知道何时释放Anchor自己的记忆?另外,'std :: shared_ptr' *会执行'operator->';我不知道你是怎么知道它没有的。 – Quuxplusone 2015-09-06 19:13:23

+0

STL是标准模板库的缩写,即容器,迭代器等。智能指针不是它的一部分。 – curiousguy 2017-03-11 05:24:10