我可以使用C ++ 11或C ++ 14(甚至C ++ 17)。假设我有一个单例对象
class MyInstance {
public:
MyInstance() {
throw std::runtime_exception("something went wrong"); // Ctor might throw
}
};
MyInstance& getInstance() {
static MyInstance obj;
return obj;
}
现在,我确保每次对getInstance
的调用都包裹在一个
try {
auto& inst = getInstance();
} catch(std::runtime_error& e) {
// do something
}
我现在想知道的是:如果在构造函数中初始化失败,并且在日志中抛出并捕获并通知用户后发生了,该怎么办...程序再次通过 在try
代码路径中并再次调用getInstance
?
我做了一些猜测,但我不知道它们是否正确:
该对象具有静态存储,因此只有在我认为之后才会尝试构建它?
返回对未构造对象的引用会得到悬挂的引用和未定义的行为吗?
是否可以使用unique_ptr
作为静态变量代替obj
来解决此问题,所以我可以多次访问指针,还可以检查对象是否已正确构造(if (uptr == TRUE)
)?
答案 0 :(得分:9)
如果构造函数抛出对象,则未初始化。因此,如果控制再次通过getInstance
,则初始化也将再次执行。
[stmt.dcl] (强调我的意思)
4动态初始化带有静态变量的块范围变量 存储持续时间或线程存储持续时间在第一个执行 时间控制通过其声明;这样的变量是 在初始化完成后认为已初始化。 如果 通过引发异常退出初始化,初始化 尚未完成,因此下次控制时将再次尝试 输入声明。如果控件进入声明 同时初始化变量时,并发 执行应等待初始化完成。如果 当变量为时,控件递归地重新输入声明 初始化后,行为是不确定的。
答案 1 :(得分:9)
[stmt.dcl]/4:
具有静态存储持续时间或线程存储持续时间的块范围变量的动态初始化是在控件第一次通过其声明时执行的;此类变量在初始化完成后即被初始化。 如果初始化由于引发异常而退出,则初始化未完成,因此下次控件进入声明时将再次尝试。 [..]
无需“猜测”;您可以put a std::cout
trace inside MyInstance::MyInstance()
and call getInstance()
twice。 ?
也不需要智能指针;该对象存在或不存在,并且在声明不存在该对象之后无法在getInstance()
内继续进行操作,因为您引发了异常!
顺便说一下,它是std::runtime_error
,而不是std::runtime_exception
。