下面是一些示例代码(在线 here ):
#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
}
我看到的(在多个编译器下)是当 nonDeletingSharedFoo
被重置时, enable_shared_from_this
内部使用的 weak_ptr
过期,所以后续调用 shared_from_this
失败。
我希望 nonDeletingSharedFoo
有一个完全独立于 sharedFoo
的引用计数,因为它是从原始指针构造的,但显然它仍然影响 的弱计数code>Foo
对象的内部 weak_ptr
。我假设这是因为 shared_ptr
构造函数和/或析构函数在指向的类型实现 enable_shared_from_this
时做了一些特殊的事情。
那么这段代码是否违反了标准?是否有解决方案,或者只是不可能在实现 enable_shared_from_this
的对象上拥有多个 shared_ptr
“系列”?
最佳答案
你在这里处于灰色地带:enable_shared_from_this
通常通过 shared_ptr
构造函数来实现,这些构造函数拥有指向从 enable_shared_from_this 派生的对象的原始指针的所有权
设置包含在对象内的 weak_ptr
。因此,稍后对 shared_from_this()
的调用会返回一些内容。当您“重新设置”sharedFoo
时,原始的 weak_ptr
值将被覆盖,因此当您最终调用 shared_from_this
时它包含一个过期值。
标准可能禁止这种行为,但我认为更可能是允许这种行为的意图,并且在这个公认的小众案例中,所有权的语义有点不明确。该标准确实指出“创建唯一指针的 shared_ptr
构造函数可以检测 enable_shared_from_this
基的存在并将新创建的 shared_ptr
分配给它的__weak_this
成员。” ([util.smartptr.enab]/11)。尽管注释是非规范性的,但我认为它说明了标准的意图。
您可以通过创建一个不共享所有权但仍然指向 sharedFoo
的真正空的 shared_ptr
来完全避免该问题:
std::shared_ptr<Foo> nonDeletingSharedFoo(std::shared_ptr<Foo>(), sharedFoo.get());
这利用了“别名”构造函数:
template<class Y> shared_ptr(const shared_ptr<Y>& r, T* p) noexcept;
它创建一个与 r
共享所有权的 shared_ptr
,在本例中是一个空的默认构造的 shared_ptr
,并指向 p
。结果是一个空的(非拥有的)shared_ptr
,它指向与 sharedFoo
相同的对象。
您有责任确保此类非拥有指针在引用对象的生命周期结束后永远不会被解除引用。清理设计可能会更好,这样您要么真正共享所有权,要么使用原始指针而不是“非拥有 shared_ptr”技巧。
关于c++ - 在同一个对象上创建多个 shared_ptr "families"时 shared_from_this 的意外行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29259500/