c++11 - Enable_shared_from_this 对象的两步构造需要将 std::shared_ptr<self> 传递给构造函数中创建的子级

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

我知道额外的初始化方法是邪恶的,因为它们为半构造对象留下了一个非常令人讨厌的选择,因此所有方法都需要检查这一点。但这种情况又如何呢?

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 尚未使用指向配置实例的共享指针进行初始化,因此构造函数不知道如何创建 weak_from_this 并仅引发 std: :bad_weak_ptr 异常,因为在构造函数调用时没有有效的 std::shared_ptr 指向 this

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

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

最佳答案

如果您查看 this question 的答案,有充分的理由说明为什么外部初始化函数是必要的。然而,你正确地写了

I know that additional initialization methods are evil, as they leave a very nasty option for having object half-constructed and as result all methods needs to check for this.

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

因此,一种方法是创建 foo private 的(非复制/非移动)构造函数,并创建一个用于创建对象的可变静态工厂方法,然后调用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;
    }

请注意,这个单一方法可用于所有构造函数,无论其签名如何。此外,由于它是静态的,因此它位于构造函数的外部,并且不存在您的问题中的问题。

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

int main() {
    auto f0 = foo::create();
    auto f1 = foo::create(2);
    // Next line doesn't compile if uncommented
    // foo f2; 
}

请注意,没有此方法就不可能创建对象,并且该接口(interface)甚至不包含 init

关于c++11 - Enable_shared_from_this 对象的两步构造需要将 std::shared_ptr<self> 传递给构造函数中创建的子级,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39653239/

相关文章:

c++ - 从(成员)函数推导出类型

在 if/else block 中进行 Java 转换

php - 规划大型 Yii2 应用程序

c++ - 智能指针而不是指针

c++ - 非多态继承对性能/内存使用有影响吗?

c++ - 是否可以从一组定义的数字中选择随机数

c++ - C++11 中的 auto 关键字奇怪行为

design-patterns - 为什么是死亡钻石而不是死亡三角形

c++ - 如何在 C 中包装返回智能指针的 C++ 函数?

c++ - 如何将多个替代模式之一与 C++11 正则表达式匹配