有一个通过实现指针实现对象可达性的循环,也就是说,您可以按照这些对象的私有实现内部使用的指针并回到您开始的地方: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是new (buff) T在std::make_shared<T>()中的结果。
无论哪种情况,都可以从元信息中获取p的值。
[请注意,要删除的值p永远不会从存储在任何特定std::shared_ptr<U>实例中的U*指针值中获得,因为这样的指针值可能不是“可删除的”,因为析构函数可能不是虚拟(只要指针参数std::shared_ptr<U> 具有正确的静态类型: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。
但是指针的循环不是问题,只有所有权的循环,因为它暗示“由生命控制的自我所有权”,即“只有当我的生命结束时我才会自毁”(也就是“我将在我将进入析构函数"),荒谬。
这里没有所有权,因为弱引用只拥有元信息,而不是信息。