enable_shared_from_this 对象的两步构造需要将 std::shared_ptr<self> 传递给在构造函数中创建的子对象
Two step constructions for enable_shared_from_this object that needs to pass std::shared_ptr<self> to children created in constructor
我知道额外的初始化方法是 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
尚未使用指向配置实例的共享指针进行初始化,因此构造函数不知道如何创建 weak_from_this
并且只是引发 std::bad_weak_ptr
异常,因为没有有效的 [=14] =] 在构造函数调用时指向 this
。
问题是:我怎样才能避免整个事情发生?我相信我看到的唯一方法是添加单独的初始化方法,正如我已经提到的 evil...
关于实际代码的说明:构造函数从外部源加载 cfg_item
。假定所有 cfg_item
在 config
的整个生命周期内都可用。返回 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.
可以减少这个问题。假设您有一个 class foo
,协议规定每次构造 foo
对象时,都需要调用 foo::init()
。显然,这是一个脆弱的 class(客户端代码最终会省略对 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;
}
请注意,此单一方法可用于所有构造函数,无论其签名如何。此外,由于它是 static
,它在构造函数外部,并且没有您问题中的问题。
您可以按如下方式使用:
int main() {
auto f0 = foo::create();
auto f1 = foo::create(2);
// Next line doesn't compile if uncommented
// foo f2;
}
请注意,没有这个方法是不可能创建对象的,而且接口甚至不包含init
。
我知道额外的初始化方法是 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
尚未使用指向配置实例的共享指针进行初始化,因此构造函数不知道如何创建 weak_from_this
并且只是引发 std::bad_weak_ptr
异常,因为没有有效的 [=14] =] 在构造函数调用时指向 this
。
问题是:我怎样才能避免整个事情发生?我相信我看到的唯一方法是添加单独的初始化方法,正如我已经提到的 evil...
关于实际代码的说明:构造函数从外部源加载 cfg_item
。假定所有 cfg_item
在 config
的整个生命周期内都可用。返回 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.
可以减少这个问题。假设您有一个 class foo
,协议规定每次构造 foo
对象时,都需要调用 foo::init()
。显然,这是一个脆弱的 class(客户端代码最终会省略对 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;
}
请注意,此单一方法可用于所有构造函数,无论其签名如何。此外,由于它是 static
,它在构造函数外部,并且没有您问题中的问题。
您可以按如下方式使用:
int main() {
auto f0 = foo::create();
auto f1 = foo::create(2);
// Next line doesn't compile if uncommented
// foo f2;
}
请注意,没有这个方法是不可能创建对象的,而且接口甚至不包含init
。