【问题标题】:std::make_shared(), std::weak_ptr and cyclic referencesstd::make_shared()、std::weak_ptr 和循环引用
【发布时间】:2017-06-28 15:22:09
【问题描述】:

我的问题是关于这个声明:

如果任何 std::weak_ptr 在所有共享所有者的生命周期结束后引用由 std::make_shared 创建的控制块,则 T 占用的内存将持续存在,直到所有弱所有者也被销毁,如果 sizeof( T) 很大。 Source

我读到here,这个对象一直存在到最后一个weak_ptr 存在。 它是释放使用 make_shared 生成的对象,循环引用 self 还是将永远存在于内存中?

例如:

struct A
{
    std::weak_ptr<A> parent;
}

void fn()
{
    auto a=std::make_shared<A>();
    a->parent = a;
} // Will it destroy here or not?

【问题讨论】:

    标签: c++ c++11 shared-ptr weak-ptr make-shared


    【解决方案1】:

    它被摧毁了。这也是weak_ptr 存在的原因之一。

    a被销毁时,引用计数器变为0,所以对象被销毁。这意味着调用了对象的析构函数,这也销毁了a-&gt;parent

    不要将 destructiondeallocation 混淆。当引用计数器变为 0,或没有 shared_ptr 拥有该对象时,该对象被销毁。如果有任何 weak_ptr 指向控制块,则内存不会deallocated - 因为该对象是用std::make_shared 分配的 - 但该对象肯定是destroyed.

    【讨论】:

    • 比我的解释清楚多了,最后一段第一句+1。
    【解决方案2】:

    问题涉及:

    如果任何 std::weak_ptr 在所有共享所有者的生命周期结束后引用由 std::make_shared 创建的控制块,则 T 占用的内存将持续存在,直到所有弱所有者也被销毁,如果 sizeof( T) 很大

    有点像

    std::weak_ptr<A> global;
    
    void foo()
    {
        auto a = std::make_shared<A>();
        global = a;
    }
    

    所以global 指向a 的控制块。 这里即使a被销毁,a使用的内存仍然存在,以允许控制块存在。

    【讨论】:

      【解决方案3】:

      有一个通过实现指针实现对象可达性的循环,也就是说,您可以按照这些对象的私有实现内部使用的指针并回到您开始的地方:a-&gt;parent 包含一个指向由std::shared_ptrstd::make_shared 创建的元信息(控制块)的指针。

      当然,std::make_shared 有望通过将元信息和托管对象放在一起来最小化动态内存分配的数量,但这与托管对象何时被销毁无关(这是唯一可观察到的方面,因为没有使用了特定于类的operator new/operator delete)。因此,控制块是否与托管对象并置,或者是否具有指向该对象的单独分配的指针,是无关紧要的。

      除了少数退化的情况(智能指针是用不释放任何东西的假删除器构造的)之外,最后一个共享拥有智能指针的生命周期结束会导致删除器运行,通常:

      • delete p;的形式运行,其中pstd::shared_ptr&lt;U&gt;的构造函数的T*类型的参数,
      • p-&gt;~T()形式,其中pnew (buff) Tstd::make_shared&lt;T&gt;()中的结果。

      无论哪种情况,都可以从元信息中获取p的值。

      [请注意,要删除的值p永远不会从存储在任何特定std::shared_ptr&lt;U&gt;实例中的U*指针值中获得,因为这样的指针值可能不是“可删除的”,因为析构函数可能不是虚拟(只要指针参数std::shared_ptr&lt;U&gt; 具有正确的静态类型:delete p; 就足够了,其中p 是传递给模板化构造函数的类型的值),因为指针可能指向成员子对象或一个完全不同的完整对象,如果使用别名构造函数来构造另一个具有共享所有权的std::shared_ptr。]

      因此,拥有一个指向控制块的指针通常可以让我们恢复一个指向受控对象的指针,尽管无法通过公共接口获得指向完整对象的指针(除非将指针传递给删除器,因此在 C++ 中恢复指针的唯一方法,如果它丢失了,将传递一个自定义删除器并等待它被调用)。指针当然可以通过在内存表示中导航来恢复(尽管导航可能需要使用dynamic_cast 到编译时未知的类型,只要调试器知道所有派生类就可以做到这一点)。

      所以我们有循环:

      a
      a->parent
         parent->control_block
                 control_block.deleter (virtual call or stored function)
                               deleter.a
      

      如果指针存储在动态创建的删除器中,这是创建std::shared_ptr&lt;U&gt;(T*)所必需的,或者

      a
      a->parent
         parent->control_block
                 control_block.buffer
      

      对于使用单个分配 make_shared 创建的对象:该对象是在该缓冲区内构造的,因此 &amp;control_block.buffer == a

      但是指针的循环不是问题,只有所有权的循环,因为它暗示“由生命控制的自我所有权”,即“只有当我的生命结束时我才会自毁”(也就是“我将在我将进入析构函数"),荒谬。

      这里没有所有权,因为弱引用只拥有元信息,而不是信息

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-12-17
        • 1970-01-01
        • 2014-08-10
        • 1970-01-01
        • 2016-08-03
        • 2016-07-31
        • 1970-01-01
        • 2018-05-23
        相关资源
        最近更新 更多