这个技巧在构造函数中调用shared_from_this(),是否存在风险?

9

给C++专家的问题。

我们都知道在类构造函数中调用shared_from_this()会导致bad_weak_ptr异常,因为该实例尚未创建任何shared_ptr。

为了解决这个问题,我想到了这个技巧:

class MyClass : public std::enable_shared_from_this<MyClass>
{
public:
    MyClass() {}

    MyClass( const MyClass& parent )
    {
        // Create a temporary shared pointer with a null-deleter
        // to prevent the instance from being destroyed when it
        // goes out of scope:
        auto ptr = std::shared_ptr<MyClass>( this, [](MyClass*){} );

        // We can now call shared_from_this() in the constructor:
        parent->addChild( shared_from_this() );
    }

    virtual ~MyClass() {}
};

有人认为这不安全,因为对象尚未完全形成。他是对的吗?
我并未使用“this”来访问成员变量或函数。此外,只要我使用初始化列表,所有成员变量都已经被初始化。我不明白这个技巧怎么可能不安全。
编辑:事实证明,这个技巧确实会产生不必要的副作用。"shared_from_this()"将指向临时的"shared_ptr",如果你不小心,我的示例代码中的父子关系将会被破坏。 "enable_shared_from_this()"的实现根本不允许这样做。感谢Sehe指出了正确的方向。

1
最好使用静态工厂函数调用make_shared并执行添加操作来处理。这样就不会有正确性的歧义了。 - user4581301
一种让那个东西爆炸的方法是 MyClass temp(myparent);,局部变量刚刚被塞进了一个共享指针。 - user4581301
这确实是我以前处理这个问题的方式(见下文),但我希望能够让它“正常工作”。 static std::shared_ptr<MyClass> create() { /* “initialize”将包含对shared_from_this()的调用 */ auto ptr = std::make_shared<MyClass>(); ptr->initialize(); return ptr; } - Paul Houx
1个回答

6

这并不危险。

文档限制为:cppreference

在调用shared_from_this之前,必须至少有一个std::shared_ptrp拥有*this

没有地方说它不能从构造函数内部使用 /因此/。

那只是一个典型的例子。这是因为在正常情况下,在T构造函数退出之前,make_sharedshared_pointer<T>(new T)无法完成。

警告:对象尚未完全形成,因此您不能合法地调用任何虚拟方法(会导致未定义行为)。


指南 由于可能错误使用此类(例如,使用shared_ptr<T>(new T)创建具有相同基础指针值的第二个shared_ptr... oops),因此您应该首选防止这种情况发生的设计。

使用返回shared_ptr<T>的友元工厂函数可能是一种方法。

--> 另请参见成功的陷阱


感谢您的解释。所以如果我理解正确,使用临时shared_ptr的技巧并不危险,而是在调用addChild方法时,如果MyClass从某个基类派生并且具有MyClass :: addChild(const MyClass&child)override函数。关于在指南部分提到拥有两个shared_ptr并不真正发挥作用,因为第一个是临时的,不会删除实例。 - Paul Houx
是的。如果调用者不知道构造函数“seeds”shared_from_this,它就会发挥作用。最好避免这种可能性。 - sehe
2
现在措辞不同了:“特别地,在*this构造期间不能调用shared_from_this”。 - Mikhail

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接