在同一对象上创建多个shared_ptr"系列"时,shared_from_this的意外行为

dlf*_*dlf 5 c++ shared-ptr weak-ptr

这是一些示例代码(在线):

#include <memory>

struct Foo : public std::enable_shared_from_this<Foo> {};

void example()
{
    auto sharedFoo = std::make_shared<Foo>();
    std::shared_ptr<Foo> nonDeletingSharedFoo(sharedFoo.get(), [](void*){});
    nonDeletingSharedFoo.reset();
    sharedFoo->shared_from_this(); // throws std::bad_weak_ptr
}
Run Code Online (Sandbox Code Playgroud)

我所看到的(在多个编译器下)是当nonDeletingSharedFoo重置时,weak_ptr内部使用的是enable_shared_from_thisexpires,因此后续调用shared_from_this失败.

我希望nonDeletingSharedFoo有一个完全独立的引用计数,sharedFoo因为它是从原始指针构造的,但显然它仍然影响Foo对象内部的弱计数weak_ptr.我假设这是因为shared_ptr构造函数和/或析构函数在指向类型实现时执行了一些特殊操作enable_shared_from_this.

那么这段代码违反了标准吗?有没有解决方案,或者只是不可能在shared_ptr实现的对象上有多个"系列" enable_shared_from_this

Cas*_*sey 2

您处于此处的灰色区域:enable_shared_from_this通常通过构造函数来实现,该构造函数获取指向从对象内包含的set ashared_ptr派生的对象的原始指针的所有权。因此后来打电话要求归还一些东西。当您“重新设置父级”时,原始值将被覆盖,以便在您最终调用 时它包含过期值。enable_shared_from_thisweak_ptrshared_from_this()sharedFooweak_ptrshared_from_this

标准可能禁止这种行为,但我认为更可能的是允许这种行为的意图,并且在这个公认的利基案例中,所有权的语义有点未指定。该标准确实指出“shared_ptr创建唯一指针的构造函数可以检测enable_shared_from_this基数的存在并将新创建的指针分配shared_ptr给其__weak_this成员。” ([util.smartptr.enab]/11)。尽管注释是非规范性的,但我认为它说明了标准的意图。

shared_ptr您可以通过创建一个真正空的、不共享所有权但仍然指向的来完全避免该问题sharedFoo

std::shared_ptr<Foo> nonDeletingSharedFoo(std::shared_ptr<Foo>(), sharedFoo.get());
Run Code Online (Sandbox Code Playgroud)

这利用了“别名”构造函数:

template<class Y> shared_ptr(const shared_ptr<Y>& r, T* p) noexcept;
Run Code Online (Sandbox Code Playgroud)

它创建了shared_ptr与 共享所有权的r,在本例中是一个空的默认构造的shared_ptr,并指向p。结果是一个空(非拥有),shared_ptr它指向与 相同的对象sharedFoo

您有责任确保此类非拥有指针在引用对象的生命周期结束后永远不会被取消引用。清理设计可能会更好,以便您真正共享所有权,或者使用原始指针而不是“非拥有的shared_ptr”黑客。