为什么shared_ptr需要保存weak_ptr的引用计数?

时间:2018-03-31 08:28:06

标签: c++ c++11 shared-ptr reference-counting

引自C ++ Primer $ 12.1.6:

  

weak_ptr(表12.5)是一个智能指针,不能控制生命周期   它指向的对象。相反,weak_ptr指向由其管理的对象   一个shared_ptr。将weak_ptr绑定到shared_ptr 不会更改   引用计数shared_ptr 。一旦最后shared_ptr指向   对象消失,对象本身将被删除。 即使是,该对象也将被删除   有weak_ptrs指向它 - 因为名称weak_ptr,它捕获了   认为weak_ptr分享其对象“弱”。

但是,我读过article说:

  

使用make_shared更有效率。 shared_ptr实现必须将管理信息保存在由引用给定对象的所有shared_ptrs和weak_ptrs共享的控制块中。特别是,该管家信息不仅包括一个而且包括两个参考计数:

     
      
  1. “强引用”计数用于跟踪当前使对象保持活动状态的shared_ptrs数。当最后一个强引用消失时,共享对象被销毁(并可能被解除分配)。

  2.   
  3. “弱引用”计数用于跟踪当前正在观察对象的weak_ptrs的数量。 当最后一个弱引用消失时,共享内务控制块被销毁并解除分配(并且如果共享对象尚未释放,则会解除分配)。

  4.   

据我所知,shared_ptr创建的make_shared与这些引用计数在同一control block内。因此,对象直到最后{{1}才会被释放过期。

问题:

  1. Primer是否错误?因为weak_ptr实际上会影响该对象的生命周期。
  2. 为什么weak_ptr需要跟踪它的弱引用?weak_ptr可以通过检查控制块中的强引用来判断对象是否存在,所以我认为控制块没有需要跟踪弱参考
  3. 只是出于好奇,shared_ptr创建的控制块是什么样的?是这样的:

    shared_ptr

4 个答案:

答案 0 :(得分:15)

引用计数控制指向对象的生命周期。弱计数不会,但 控制(或参与控制)控制块的生命周期

如果引用计数转到0,则对象已销毁,但不一定已取消分配。当弱计数转到0时(或当引用计数转到0时,如果发生这种情况时没有weak_ptr),则控制块将被销毁并取消分配,并且如果对象的存储空间尚未释放,则将其解除分配。

销毁解除分配指向对象之间的分离是一个您不需要关心的实现细节,但它是由使用{{ 1}}。

如果你这样做

make_shared

shared_ptr<int> myPtr(new int{10}); 分配存储空间,然后将其传递给int构造函数,该构造函数分别为控制块分配存储空间。在这种情况下,shared_ptr的存储空间可以尽快解除分配:一旦引用计数达到int,即使仍然存在弱计数。

如果你这样做

0

然后auto myPtr = make_shared<int>(10); 可能会执行优化,它会一次性为make_shared和控制块分配存储空间。这意味着在控制块的存储也可以解除分配之前,不能释放int的存储空间。 int的生命周期在引用计数达到int时结束,但在弱计数达到0之前,其存储空间不会被释放。

现在清楚了吗?

答案 1 :(得分:5)

weak_ptr需要指向可以判断对象是否存在的东西,因此它知道它是否可以转换为shared_ptr。因此,需要一个小物体来保存这些信息。

当删除最后一周的day_ptr(或shared_ptr)时,需要销毁此内务控制块。因此,它必须保持shared_ptr和week_ptr的计数。

请注意,管家控制块与ptr指向的对象不同,因此week_ptr不会影响对象的生命周期。

根据您希望的行为,有许多不同的方法来实现智能指针。如果你想了解更多,我会推荐&#34;现代C ++设计&#34;由Alexandrescu(https://www.amazon.com/Modern-Design-Generic-Programming-Patterns/dp/0201704315

答案 2 :(得分:2)

weak_ptr和shared_ptr都指向包含控制块的内存。如果在shared_ptr计数器达到0后立即删除控制块(但弱计数器没有),则会留下指向垃圾内存的weak_ptrs。然后当你尝试使用weak_ptr时,它会读取释放的内存并发生坏事(UB)。

由于这个原因,只要任何weak_ptr可能试图读取它,控制块必须保持活动状态(分配和构造,不销毁或解除分配)。

主(指向)对象将被销毁,并且一旦共享计数器达到0,可能(希望)被解除分配。当两个计数器都达到0时,控制块将被销毁并解除分配。

答案 3 :(得分:0)

一个不错的第一步是在头脑中对该概念的表象清楚,使销毁解除分配之间的区别更加明显。是一种不需要您关心的(详细提到的)无知强化步骤的详细信息。

因此,假设 SeriousObject class ,其大小约为系统内存的一半,并且在构造时可以控制鼠标,并且我们认为这是被破坏的隐性副作用,而不是 SeriousObject解除分配 实例 。在这种情况下,尽管鼠标控件返回了,但您仍然只有一半的可用内存。最坏的情况是,在代码的某个地方存在被遗忘甚至泄漏的更严重的地方, weak_ptr ,使您的记忆变得la脚,情绪低落,并且在其余的情况下显示了50%的剩余量执行。但是,嘿,至少它没有泄漏,对吧?

我假设您在每个问题上都负有责任:

  1. 实际上不是,我敢大胆地打赌,一半的钱要花在作者发表之前仔细检查过这些文本的作者,另一半是到现在肯定已经发现了错误,它应该首先存在吗?
  2. 因为在这种情况下,未跟踪 weak_ptr ,而 shared_ptr 和控制块均已被破坏,并且至少有一个 weak_ptr 指向 object ,您认为在 {{1} } 尝试按照您的建议检查控制块中的 strong参考? ...到处都是偏头痛。
  3. 这一次,肯定不知道,我只想说:你,还有我和其他许多与编码相关的人,都忘了这个不起眼的资源,这个 uh。是的。一方面,这肯定不是毫无用处的,另一方面,这些东西也是免费的,开源的,真实的,案例,行业级质量的知识资源。继续,兄弟们!时刻到来,抓住的时刻,对我们来说,以及用鲜血和血统界定的东西,一直如此,永远永远是不可侵犯的,我们一出生就享有权利,我们通过光荣的奴役特权,以及我们为了和所以超越了死亡约束!

PS(实际上更像是BTW)

我个人的困惑不在于 weak_ptr ,而是决定在对象生命周期的特定阶段进行这种优化,我指的是一次分配类型优化并进行详细说明,我的意思是选择优化最短单次出现的寿命阶段,同时接受以技术和行为方面的副作用为代价付出的代价,而作为交换,他们实际上却毫不费力地收拾了几只山羊粪便,作为其劳动成果。 Pff