shared_ptr
使用额外的“计数器”对象(又名“共享计数”或“控制块”)来存储引用计数。
(顺便说一句:那个“计数器”对象还存储删除器。)
Every shared_ptr
and weak_ptr
包含一个指向实际指针的指针,以及另一个指向“计数器”对象的指针。
实施weak_ptr
,“counter”对象存储两个不同的计数器:
- “使用次数”是指使用次数
shared_ptr
指向该对象的实例。
- “弱计数”是指
weak_ptr
指向该对象的实例,如果“使用计数”仍然 > 0,则加一。
当“使用计数”达到零时,指针被删除。
当“弱计数”达到零时,“计数器”辅助对象将被删除(这意味着“使用计数”也必须为零,见上文)。
当你试图获得一个shared_ptr
from a weak_ptr
,库自动检查“使用计数”,如果它 > 0,则递增它。如果成功的话你会得到你的shared_ptr
。如果“使用计数”已经为零,您会得到一个空的shared_ptr
实例代替。
EDIT:现在,为什么当两个计数都降至零时,他们要向弱计数加一,而不是仅仅释放“计数器”对象?好问题。
另一种方法是当“使用计数”和“弱计数”都降至零时删除“计数器”对象。第一个原因是:在每个平台上原子地检查两个(指针大小的)计数器是不可能的,即使在平台上,它也比仅检查一个计数器更复杂。
另一个原因是删除器必须保持有效直到执行完毕。由于删除器存储在“计数器”对象中,这意味着“计数器”对象必须保持有效。考虑一下如果有一个会发生什么shared_ptr
和一个weak_ptr
到某个对象,并且它们在并发线程中同时重置。让我们说shared_ptr
首先。它将“使用计数”减少到零,并开始执行删除器。现在weak_ptr
将“弱计数”减少到零,并发现“使用计数”也为零。因此它删除了“计数器”对象以及删除器。当删除器仍在运行时。
当然,可以采用不同的方法来确保“计数器”对象保持活动状态,但我认为将“弱计数”增加一是一种非常优雅且直观的解决方案。 “弱计数”成为“计数器”对象的引用计数。自从shared_ptr
s 也引用计数器对象,它们也必须增加“弱计数”。
一个可能更直观的解决方案是增加每个单独的“弱计数”shared_ptr
,因为每一个shared_ptr
hold 是对“计数器”对象的引用。
为所有人添加一个shared_ptr
实例只是一种优化(在复制/分配时保存一个原子增量/减量shared_ptr
实例)。