存在一种通过实现指针的对象可达性循环,也就是说,您可以跟随这些对象的私有实现中使用的指针并返回到起始点:a->parent
包含一个指向由std::shared_ptr
或std::make_shared
创建的元信息(控制块)的指针。
当然,std::make_shared
旨在通过组合元信息和托管对象来最小化动态内存分配的数量,但这与何时销毁托管对象无关(这是唯一可观察的方面,因为没有使用类特定的operator new
/operator delete
)。因此,控制块是与托管对象共位还是具有指向单独分配的该对象的指针都是无关紧要的。
除了少数退化情况(其中智能指针使用不释放任何内容的虚拟删除器构造),最后一个共享所有智能指针的生命周期结束会导致运行删除器,通常如下:
- 以
delete p;
的形式运行,其中p
是std :: shared_ptr<U>
构造函数的类型为T *
的参数,
- 或形如
p->~T()
,其中p
是std :: 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
。
但指针循环不是问题,只有所有权循环才是问题,因为它意味着“自我所有权由生命周期控制”,也就是说,“只有当我的生命周期结束时,我才会销毁自己”(又称“当我进入析构函数时,我将进入析构函数”),这是荒谬的。
在这里,没有所有权,因为弱引用仅拥有元信息,而不拥有信息。