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

Posted

技术标签:

【中文标题】构造函数应该如何报告错误?指向外部标志的指针?【英文标题】:How should a constructor report errors? Pointers to external flags? 【发布时间】:2011-09-12 14:10:22 【问题描述】:

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

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

这是正确的做法吗?

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

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

【问题讨论】:

【参考方案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;
        
    
;

现在,使用异常的代码可以调用多参数构造函数(并使对象处于准备使用状态),而厌恶异常的代码可以调用默认构造函数,后跟nothrow_init(并与事实上,如果nothrow_init 失败,他们手上有一个无法使用的对象,他们有责任确保他们不使用它)。

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

【讨论】:

【参考方案2】:

您的代码很危险,因为它允许出现以下不良用例:

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 获得此权利,那么对象的持续存在就是程序有效性的证明。

【讨论】:

【参考方案3】:

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

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

【讨论】:

-1 因为...很多东西。例外是一种工具。说无视一切并锤炼你的信念,你应该如何编程是另一回事。一句话回复很少有用。异常作为控制流??等等……等等……请扩展你的推理,我会投票并说我错了。 查看我的答案和链接的 GOTW。例外并不适用于所有情况,但对于它们所适用的情况,有 NO 有效的替代方案。失败的对象构造就是其中之一。 在构造函数中不做更适合流控制的事情怎么样。有时在那里做这些事情是完全有效的。这显然不是这种情况。真的,如果有机会,你们会抛出 CHeyWeJustAddedANumberException :P 我不知道你在说什么。我没有提倡将异常作为控制流,也没有展示任何其他 OTT 提倡的异常。然而,异常对于发出错误信号非常有用,并且比返回码要好得多,应该改为使用。

以上是关于构造函数应该如何报告错误?指向外部标志的指针?的主要内容,如果未能解决你的问题,请参考以下文章

如何获得指向类的复制构造函数的成员函数指针?

类构造函数中指向 QProcess 对象的指针使 qt GUI 崩溃

使用移动构造函数时如何获取指向新变量的指针

如何在构造函数中初始化指向字符串的指针数组[重复]

将指向外部类模板的成员函数的指针传递给嵌套类

在 C# String 构造函数 String(Char*) 中,为啥构造函数不期望指向字符数组的指针?