构造函数应该如何报告错误?指向外部标志的指针?

时间:2011-09-12 14:10:23

标签: c++ class reference

我正在努力重构一些旧的C风格代码,使其更符合C ++代码。我对C ++还有点新鲜

我正在处理的代码示例如下

Errmsg foo{
   ErrMsg err = NoError;
   /*
    Some Processing
   */
  err = foo_cleanup(err,/* some parameters*/);
   /*
    Some More Processing
   */
  return err;
}

我正在考虑开发一个类,以便

class foo_class
{
 public:
   foo_class(Errmsg errmsg_init&,/*Some other arguments */ ):
      errmsg(&errmsg_init),
      /*Initialize other parameters */{}

   void foo_cleanup (/*Other parameters*/);
   // same functionality, but since the source variable is available, 
   // it can be modified without having to return any variable

  ~foo_class(){foo_cleanup(/*Parameters*/);}

   /*
   Member functions
   */

 private:
   Errmsg* errmsg;
   /*Some other parameters */
};

Errmsg foo{
   ErrMsg err = NoError; 
   foo_class foo_obj(err);
   /*
    Some Processing
   */

  // The class would be 
  //cleaned up before returning 
  // and the err variable would be 
  //modified in the destructor
  return err;
}

虽然我已经能够使用类似于这种方法的东西,但我不知道这是否可以移植。

这是正确的做法吗?

如果没有,我是否只使用指针初始化类而不是通过引用传递错误消息变量?或者我还能做些什么吗?

我不能在当前阶段使用异常,因为有许多来自/来自外部代码的函数调用仍使用“返回错误消息”方法。

3 个答案:

答案 0 :(得分:1)

您的代码很危险,因为它允许使用错误的用例:

return new FooClass (local_error_code_variable);

不要尝试使用返回码来表示构造函数失败。 你不能。使用例外。

您可以在返回代码中包含异常,反之亦然。

class NewAPIClass {
    NewAPIClass () {
        error_code err = old_api_function ();
        if (OLD_API_OK != err)
            throw NewAPIException (err);
    }
}

extern "C" error_code new_api_callback_function (argument arg) {
    try {
        NewAPIClass object;
        object .do_work ();
    }
    catch (...) {
        return OLD_API_ERROR;
    }
}

int main () {
    old_api_install_callback (new_api_callback_function);
}

例外非常重要。有许多好的GOTW articles,你应该把它们理解为成为C ++开发人员的目标。

编写新代码以正确使用异常。无论何时遇到旧代码和新代码之间的边界,都要转换错误类型。

顺便说一句,例外是 唯一 构造函数失败的合理方式。这是RAII的一部分,这是使C ++如此强大的关键。构造函数建立不变量和异常表示无法满足后置条件 - 将它们放在一起这就是重要的哲学: 在C ++中只有有效对象存在 ,如果你得到的话通过利用RAII这一权利,对象的继续存在就证明了程序的有效性。

答案 1 :(得分:1)

暂且不说,如果可能的话,你应该修改调用代码以便异常正常,你可以使用两阶段构造习语:

struct foo_class {
    foo_class() { } // default constructor cannot fail
    Errmsg nothrow_init(/* other params */) throw() {
        /* initialize other parameters */
    }
    foo_class(/* other params */) {
        if (nothrow_init(/* other params */) != ERRMSG_OK) {
            throw something;
        }
    }
};

现在,异常使用代码可以调用multi-arg构造函数(并使对象处于准备使用状态),而异常代码可以调用默认构造函数,后跟nothrow_init(和如果nothrow_init失败,他们手上就有一个无法使用的物体,他们有责任确保他们不使用它。

将来,当你将代码库的其他部分带入异常使用状态时,你会发现init中的代码会调用自己可以抛出的东西。此时,您可以开始移动代码,以便nothrow_init调用可以抛出的init函数,但捕获任何异常并将其转换为错误代码。假设它是一个内部类,那么最终什么都不会使用nothrow_init,你可以删除它。

答案 2 :(得分:0)

  

我不能在当前阶段使用例外,因为有很多例外   函数调用/从外部代码调用使用“返回错误   消息“接近。”

然后先解决这个问题。然后使用例外。