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

How should a constructor report errors? Pointers to external flags?

本文关键字:外部 标志 指针 错误 何报告 报告 构造函数      更新时间:2023-10-16

我正在重构一些旧的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;
}

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

这是正确的事情吗?

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

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

如果可能的话,您应该修复调用代码,以便可以处理异常,您可以使用两阶段构造习惯:

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;
        }
    }
};

现在,使用异常的代码可以调用多参数构造函数(并使对象处于就绪状态),而避免异常的代码可以调用默认构造函数,后跟nothrow_init(并且接受这样一个事实,即如果nothrow_init失败,他们手上有一个不可用的对象,他们有责任确保他们不使用它)。

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

你的代码是危险的,因为它允许这样的坏用例:

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文章,你应该把理解它们作为一个c++开发人员的目标。

编写新代码以正确使用异常。当你遇到新旧代码之间的边界时,转换错误类型

顺便说一下,异常是构造函数失败的的合理方式。这都是RAII的一部分,而RAII是使c++如此强大的关键。构造函数建立你的不变量,而异常标志着不能满足后置条件——把它们放在一起,这里是重要的哲学:在c++中只有有效的对象应该存在,如果你通过利用RAII正确地做到了这一点,那么对象的持续存在就是程序有效性的证明。

我不能在当前阶段使用异常,因为有很多函数调用使用"返回错误"的外部代码消息"接近仍在。

然后先解决这个问题。然后使用异常