enable_shared_from_this对象的两步构造,需要将std :: shared_ptr <self>传递给构造函数中创建的子元素

时间:2016-09-23 05:32:09

标签: c++11 design-patterns smart-pointers

我知道额外的初始化方法是 evil ,因为它们为半对象构造了一个非常讨厌的选项,因此所有方法都需要检查这个。但是这种情况呢?

class config;
class cfg_item final
{
    private:
        friend class config;
        cfg_item(std::weak_ptr<config> owner) : owner(owner) { }
        std::weak_ptr<config> owner;
}
class config final : private std::enable_shared_from_this<config>
{
    public:
        config()
        {
             items.emplace(std::make_shared<cfg_item>(weak_from_this())); // Will crash!
        }
    private:
        std::vector<std::shared_ptr<cfg_item>> items;
}
int main(int argc, char * argv[])
{
    std::shared_ptr<config> cfg = std::make_shared<config>();
}

我知道为什么会崩溃。 main中的std::shared_ptr尚未使用指向config实例的共享指针进行初始化,因此构造函数不知道如何生成weak_from_this并且只引发std::bad_weak_ptr异常,因为没有有效{{1}在构造函数的调用时指向std::shared_ptr

问题是:我怎样才能避免整件事?我相信我看到的唯一方法是添加单独的初始化方法,这是 evil ,正如我已经提到的那样......

关于实际代码的说明:构造函数从外部源加载this。假设所有cfg_itemcfg_item的整个生命周期内都可用。弱指针返回config是强制性的,因为config必须将对其执行的所有更改推回cfg_item以保存到外部源

1 个答案:

答案 0 :(得分:1)

如果你看一下this question的答案,有充分的理由说明为什么需要外部初始化函数。但是,你正确地写了

  

我知道额外的初始化方法是邪恶的,因为它们为对象半构造留下了非常讨厌的选项,因此所有方法都需要检查这一点。

可以减少这个问题。假设您有一个类foo,其协议是每次构造foo对象时,都需要调用foo::init()。显然,这是一个脆弱的类(客户端代码最终会省略对init()的调用)。

因此,一种方法是创建foo private的(非复制/非移动)构造函数,并创建一个创建对象的可变参数静态工厂方法,然后调用{{1} }:

init()

在以下代码中

#include <utility>

class foo { 
private:
    foo() {}
    foo(int) {}
    void init() {}

public:
    template<typename ...Args>
    static foo create(Args &&...args) {
        foo f{std::forward<Args>(args)...};
        f.init();
        return f;
    }
};

请注意,此单一方法可用于所有构造函数,无论其签名如何。此外,由于它是 template<typename ...Args> static foo create(Args &&...args) { foo f{std::forward<Args>(args)...}; f.init(); return f; } ,因此它在构造函数的外部,并且在您的问题中没有问题。

您可以按如下方式使用它:

static

请注意,如果没有此方法,则无法创建对象,并且界面甚至不包含int main() { auto f0 = foo::create(); auto f1 = foo::create(2); // Next line doesn't compile if uncommented // foo f2; }