是否有针对类M
的私有类成员C
的延迟初始化的最佳做法?例如:
class C {
public:
C();
// This works properly without m, and maybe called at any time,
// even before startWork was called.
someSimpleStuff();
// Called single time, once param is known and work can be started.
startWork(int param);
// Uses m. Called multiple times.
// Guaranteed to only be called after startWork was called
doProcessing();
private:
M m;
};
class M {
M(int param);
};
无法构建类C
的对象,因为M
没有默认初始值设定项。
如果您可以修改M
的实施,则可以向init
添加M
方法,并使其构造函数不接受任何参数,允许构造类C
的对象。
如果没有,您可以将C
成员m
包裹在std::unique_ptr
中,并在可能的情况下构建它。
但是,这两种解决方案都容易出现错误,这些错误会在运行时捕获。是否有一些练习可以确保编译时 m
仅在初始化后使用?
限制:C类的一个对象被传递给使用其公共接口的外部代码,因此C的公共方法不能分成多个类。
答案 0 :(得分:5)
最佳做法是从不使用延迟初始化。
在您的情况下,抛弃C
的默认构造函数并将其替换为C(int param) : m(param){}
。也就是说,类成员使用基本成员初始化在构造点初始化。
使用延迟初始化意味着您的对象可能处于未定义状态,并且实现并发等事情更难。
答案 1 :(得分:2)
#define ENABLE_THREAD_SAFETY
class C {
public:
C();
// This works properly without m, and maybe called at any time,
// even before startWork was called.
someSimpleStuff();
// Called single time, once param is known and work can be started.
startWork(int param);
// Uses m. Called multiple times.
// Guaranteed to only be called after startWork was called
doProcessing();
M* mptr()
{
#ifdef ENABLE_THREAD_SAFETY
std::call_once(create_m_once_flag, [&] {
m = std::make_unique<M>(mparam);
});
#else
if (m == nullptr)
m = std::make_unique<M>(mparam);
#endif
return m.get();
}
private:
int mparam;
std::unique_ptr<M> m;
#ifdef ENABLE_THREAD_SAFETY
std::once_flag create_m_once_flag;
#endif
};
class M {
M(int param);
};
现在你所要做的就是直接停止使用m,然后通过mptr()来访问它。它只会在第一次使用时创建一次。
答案 2 :(得分:1)
我会选择unique_ptr ...你在哪里看到问题?使用M时,您可以轻松查看:
if(m)
m->foo();
我知道这不是编译时检查,但据我所知,目前的编译器无法检查。代码分析必须非常复杂,因为你可以在任何方法中初始化m,或者 - 如果是公共/受保护的 - 甚至在另一个文件中。编译时检查意味着延迟初始化是在编译时完成的,但延迟初始化的概念是基于运行时的。
答案 3 :(得分:1)
根据我对您的问题的理解,这是一个解决方案吗?
您将不需要M
的功能放入class D
。您创建D
对象并使用它。在您需要M
并且想要执行doProcessing()
代码后,您需要创建C
的对象,将D
传递给它并使用param
初始化它现在有了。
以下代码仅用于说明这个想法。在这种情况下,您可能不需要startWork()
成为单独的函数,其代码可以在C
的构造函数中编写
注意:我已将所有函数都清空,因此我可以编译代码以检查语法错误:)
class M
{
public:
M(int param) {}
};
class D
{
public:
D() {}
// This works properly without m, and maybe called at any time,
// even before startWork was called.
void someSimpleStuff() {}
};
class C
{
public:
C(D& d, int param) : d(d), m(param) { startWork(param); }
// Uses m. Called multiple times.
// Guaranteed to only be called after startWork was called
void doProcessing() {}
private:
// Called single time, once param is known and work can be started.
void startWork(int param) {}
D& d;
M m;
};
int main()
{
D d;
d.someSimpleStuff();
C c(d, 1337);
c.doProcessing();
c.doProcessing();
}
答案 4 :(得分:0)
问题是&#34;是否有可能在编译时检查m仅在初始化之后使用而不拆分C接口?&#34;
答案是否,您必须使用类型系统来确保在初始化之前不使用对象M,这意味着要拆分C接口。在编译时,编译器只知道对象的类型和常量表达式的值。 C不能是文字类型。所以你必须使用类型系统:你必须拆分C接口,以确保在编译时M只在初始化后使用。