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

big*_*iao 8 c++ reference-counting shared-ptr c++11

引自C++ Primer $ 12.1.6:

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

但是,我读过一篇文章说:

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

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

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

据我所知,shared_ptr创建的make_shared是与那些引用计数在同一个控制块中weak_ptr.所以在最后一个到期之前对象不会被释放.

题:

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

    template<typename T>
    class control_block
    {
       T object;
       size_t strong_refs;
       size_t weak_refs;
       void incre();
       void decre();
       //other member functions...
    };
    //And in shared_ptr:
    template<typename T>
    class shared_ptr
    {
       control_block<T> block;//Is it like this?So that the object and refs are in the same block?
       //member functions...
    };
    
    Run Code Online (Sandbox Code Playgroud)

BoB*_*ish 17

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

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

销毁取消分配指向对象之间的分离是一个您不需要关心的实现细节,但它是由使用引起的make_shared.

如果你这样做

shared_ptr<int> myPtr(new int{10});
Run Code Online (Sandbox Code Playgroud)

你为它分配存储int,然后将其传递给shared_ptr构造函数,构造函数分别为控制块分配存储空间.在这种情况下,int可以尽早解除分配的存储:一旦引用计数命中0,即使仍有弱计数.

如果你这样做

auto myPtr = make_shared<int>(10);
Run Code Online (Sandbox Code Playgroud)

然后make_shared可以执行优化,其中int一次性为控制块分配存储.这意味着int在控制块的存储也可以被解除分配之前,不能释放存储器.int当引用计数命中时0,结束的生命周期,但是在弱计数命中之前,它的存储不会被释放0.

现在清楚了吗?


mad*_*ers 7

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

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

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

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

  • 如果未跟踪weak_ptr,则需要删除控制块,然后删除最后一个shared_ptr.然后weak_ptr将指向不再存在的东西(控制块),这是不好的...... (3认同)
  • @bigxiao你不能因为它们很小而泄漏物体.控制块已分配,因此必须取消分配.你可以通过泄漏一些大的物体来泄漏大量的小物体,从而耗尽内存. (2认同)