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

3

我的问题是关于这个说法的:

如果任何一个std::weak_ptr引用了在所有shared owner的生命周期结束后由std::make_shared创建的控制块,则T占用的内存将一直持续到所有weak owner也被销毁,如果sizeof(T)很大,则可能不希望出现这种情况。来源

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

11

它被销毁了。这是为什么存在weak_ptr的原因之一。

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

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


比我的解释清晰多了,最后一段第一句话加个赞。 - Martin Bonner supports Monica

3

涉及问题:

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

0

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

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

除了少数退化情况(其中智能指针使用不释放任何内容的虚拟删除器构造),最后一个共享所有智能指针的生命周期结束会导致运行删除器,通常如下:

  • 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>具有正确的静态类型:当传递给模板化构造函数的类型的值为p时,delete 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

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接