3

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

如果任何 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?
4

3 回答 3

11

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

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

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

于 2017-06-28T15:26:55.793 回答
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仍然存在,以允许控制块存在。

于 2017-06-28T15:31:26.123 回答
0

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

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

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

  • 要运行的形式delete p;,其中是构造函数p类型的参数,T*std::shared_ptr<U>
  • or of the form p->~T(), where p is the result of new (buff) T in std::make_shared<T>().

In either case, the value of p can be obtained from the meta information.

[Note that the value p to be deleted is never obtained from the U* pointer value stored in any particular std::shared_ptr<U> instance, as such pointer value may not be "deletable", as the destructor may not be virtual (as long as the pointer argument std::shared_ptr<U> has the right static type: it's sufficient that delete p; where p is the value of the type passed to the templated constructor), as the pointer may be to a member subobject or a complete different complete object, if an aliasing constructor was used to construct another std::shared_ptr with shared ownership.]

So having a pointer to the control block usually allows one to recover a pointer to the controlled object, although that pointer to the complete object cannot be obtained through the public interface (except that the pointer is passed to the deleter, so the only way to recover the pointer in C++, if it was lost, would be to have passed a custom deleter and wait for it to be called). The pointer can certainly be recovered by navigating inside the memory representation (although that navigation might need to use dynamic_cast to a type unknown at compile time, something the debugger will be able to do as long as it knows about all derived classes).

So we have the cycle:

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

if the pointer is stored in a dynamically created deleter, as is necessary to create std::shared_ptr<U>(T*), or

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

for objects created with a single allocation make_shared: the object was constructed inside that buffer so &control_block.buffer == a.

But cycles of pointers are not an issue, only cycle of ownership as it implies "self ownership controlled by lifetime", that is "I will destruct myself only when my lifetime will be over" (aka "I will enter destructor when I will have entered destructor"), an absurdity.

Here there is no ownership as a weak reference only owns the meta information, not the information.

于 2018-06-13T05:45:21.450 回答