【问题标题】:Singleton with an object which throws in the ctor - accessing again?带有抛出 ctor 的对象的单例 - 再次访问?
【发布时间】:2019-07-09 09:53:24
【问题描述】:

我可以使用 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))?

【问题讨论】:

  • 调用getInstance()两次,将异常信息上报到命令行就很容易发现了。
  • @yeoman 如果怀疑有未定义的行为,则不会。在这种情况下,使用任何代码来检查行为实际上是没有用的。
  • @yeoman - 除非行为未定义,否则这样的实验意义不大。但是要知道它是否未定义,OP 必须在询问之前知道答案。
  • 这当然是一个有效的观点^^另一方面,如果静态变量在面对异常时具有未定义的行为,那么很多事情都行不通:)

标签: c++ exception memory-management unique-ptr


【解决方案1】:

如果构造函数抛出对象没有初始化。所以如果控制再次通过getInstance,也会再次执行初始化。

[stmt.dcl](强调我的)

4 使用静态动态初始化块范围变量 存储持续时间或线程存储持续时间首先执行 时间控制通过其声明;这样的变量是 在其初始化完成时被认为已初始化。 如果 初始化通过抛出异常退出,初始化 不完整,下次控制再试 进入声明。如果控制进入声明 在初始化变量的同时,并发 执行应等待初始化完成。如果 控制在变量是递归的时候重新进入声明 正在初始化,行为未定义。

【讨论】:

    【解决方案2】:

    [stmt.dcl]/4:在控制第一次通过其声明时执行具有静态存储持续时间或线程存储持续时间的块范围变量的动态初始化;这样的变量在其初始化完成时被认为已初始化。 如果抛出异常退出初始化,说明初始化未完成,下次控件进入声明时会再次尝试。 [..]

    无需“猜测”;你可以put a std::cout trace inside MyInstance::MyInstance() and call getInstance() twice。 ?

    也不需要智能指针;该对象要么存在,要么不存在,并且在声明后没有对象存在的情况下,无法在 getInstance() 内部继续,因为您抛出了异常!

    顺便说一下,这是std::runtime_error,而不是std::runtime_exception

    【讨论】:

    • 我在测试中做了cout 的一些事情,但由于 UB 的阴影潜伏在周围,我认为这只是一个没有实际意义的实验。标准报价清除了它。
    猜你喜欢
    • 1970-01-01
    • 2021-12-03
    • 1970-01-01
    • 2014-12-27
    • 2018-05-02
    • 1970-01-01
    • 2017-05-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多