两步到一步初始化和错误处理

时间:2012-07-20 23:09:43

标签: c++ exception-handling constructor initialization

这似乎是一个奇怪的问题。我目前正在将某些对象从两步骤移动到一步式初始化方案。基本上将.initialize() .terminate()成员函数中的内容移动到构造函数和析构函数中。

我的问题是,重要的是要知道这些类是否正确地初始化了某些依赖于外部因素的属性。

一个例子是我的Window类,它创建了一个WinAPI窗口。以前使用两步法我会initialize()返回一个是否正确创建窗口的布尔值。

if(myWindow.initialize())
{
    // proceed with application
}
else
{
    // exit
}    

无论如何都要从构造函数中继这些信息而不创建并且必须调用第二种方法,例如didMyWindowInitializeCorrectly()

起初我希望有什么东西沿着

if(Window *myWindow = new Window)
{
    // proceed with application
}
else
{
    // exit
}

但这不起作用,因为即使窗口创建失败,Window对象仍将实例化。

唯一的解决方案是让构造函数抛出异常然后捕获并继续吗?我看了很多线程,人们对C ++异常的看法似乎相当分裂,所以我不确定什么是最好的方法。

无论如何用if语句处理这种情况吗?

4 个答案:

答案 0 :(得分:5)

这归结为Exception或基于错误代码的初始化的重要论点。我通常更喜欢Exceptions来指示构造函数失败,因为当代码中的is_valid检查没有处理问题或忽略两步返回值时,问题在堆栈跟踪中变得更加明显。更清楚的是为什么初始化失败而不需要错误代码查找。那些喜欢传统C风格而不是Exception风格失败消息的人可能会在最后一点上与我不同意。

但是,匹配其余代码库的样式通常是个好主意。因此,如果代码中的其他任何地方都没有使用异常,那么在引入对象验证机制时,最好进行is_valid检查(或原始的两阶段初始化)。我更喜欢两个阶段的is_valid,就像Riateche发布的一样,因为它让用户能够选择是否/何时想要检查它是否有效,而不需要他们去查找在合法使用对象之前必须调用哪些函数。

答案 1 :(得分:2)

您可以做的一件事是创建一个静态方法来执行两步初始化,即

class Window {
  public:
     static Window* createWindow() {
          Window* w = new Window();
          if (w->isValid()) {
            return w;
          }
          delete w;
          return NULL;
     }
  private:
    Window();
    bool isValid();

};

通过这种方式,您将两步初始化封装为单向初始化。

答案 2 :(得分:1)

在这种情况下,使用例外是可以的。通常,构造函数必须始终返回有效对象或抛出异常。

如果您真的反对异常,我建议您使用此表格:

Window my_window;
if (!my_window.is_valid()) {
  cout << "fail";
  return;
}

但是单独的initialize方法会更好。

答案 3 :(得分:1)

我当然会使用异常,但如果不出于任何原因,您可以覆盖new运算符并在出错时返回NULL。 (回忆之前,别忘了释放你的记忆)。

void* T::operator new(size_t x);

编辑:忘记添加,如果你重载新的,你需要使用malloc和过载删除来保留内存。