繁体   English   中英

std :: make_shared(),std :: weak_ptr和循环引用

[英]std::make_shared(), std::weak_ptr and cyclic references

我的问题是这个说法:

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

在这里读到,这个对象一直存在,直到最后一个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?

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

a被销毁时,引用计数器变为0,因此对象被销毁 这意味着调用对象的析构函数,它也会破坏a->parent

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

问题涉及:

如果任何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使用的存储器仍然存在以允许控制块存在。

通过实现指针有一个对象可达性循环 ,也就是说,您可以跟随这些对象的私有实现中使用的指针并返回到您启动的位置: a->parent包含指向元信息的指针(控制块)由std::shared_ptrstd::make_shared

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

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

  • delete p;的形式delete p; 运行,其中pstd::shared_ptr<U>的构造函数的类型T*的参数,
  • 或者是p->~T() ,其中pstd::make_shared<T>()new (buff) T的结果。

在任何一种情况下, p的值都可以从元信息中获得。

[注意,要删除的值p永远不会从存储在任何特定std::shared_ptr<U>实例中的U*指针值获得,因为这样的指针值可能不是“可删除的”,因为析构函数可能不是虚拟的(只要指针参数std::shared_ptr<U>具有正确的静态类型: delete p;就足够了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<U>(T*) ,或者

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

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

但是指针的循环不是问题,只是所有权的循环,因为它意味着“由生命控制的自我所有权”,即“只有在我的生命将结束时我才会毁灭自己”(又名“我将在我将要进入析构函数时”)进入析构函数“),荒谬。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM