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

时间:2017-06-28 15:22:09

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

我的问题是关于这个主张:

  

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

我读了here,这个对象一直存在,直到最后一个weak_ptr存在。 它是使用make_shared制作的自由对象,循环引用自身还是永远存在于内存中?

例如:

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

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

3 个答案:

答案 0 :(得分:11)

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

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

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

答案 1 :(得分:3)

问题涉及:

  

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

就像是

#container h2:after {
  transition: all 1s ease;
  margin-left:5px;
}

#container:hover h2:after {
  margin-left:25px;
  transition: all 1s ease;
}

#container h2:after {
  content: "->";
  position: relative;

}

所以std::weak_ptr<A> global; void foo() { auto a = std::make_shared<A>(); global = a; } 指向global的控制块。 即使a被销毁,a使用的内存仍然存在,以允许控制块存在。

答案 2 :(得分:0)

通过实现指针存在对象可达性循环,也就是说,您可以按照这些对象的私有实现中使用的指针进行操作,然后返回到您开始的位置:a->parent包含指向由std::shared_ptrstd::make_shared创建的元信息(控制块)的指针。

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

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

    要运行的delete p;形式的
  • ,其中pT*的构造函数std::shared_ptr<U>的参数,
  • p->~T()形式,其中pnew (buff) Tstd::make_shared<T>()的结果。

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

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

所以有一个指向控制块的指针通常允许人们恢复指向受控对象的指针,尽管通过公共接口无法获得指向完整对象的指针(除了指针传递给删除器,所以只有这样才能在C ++中恢复指针,如果它丢失了,那就是通过一个自定义删除器并等待它被调用)。通过在内存表示中导航,当然可以恢复指针(尽管导航可能需要在编译时使用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创建的对象:该对象是在该缓冲区内构建的{{1}}。

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

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