在异常处理程序中使用'goto'是不好的风格吗?

Is using 'goto' in exception handlers bad style?

本文关键字:风格 异常处理程序 goto      更新时间:2023-10-16

自从我开始编程以来,我被教导在可以避免的情况下永远不要使用"goto"。I、 然而,我遇到了一个案例,我认为goto是保持代码干净的最简单方法。

我的代码看起来像下面的例子:

// code within a for-loop
// ...
// ...
try{
      if(!ifstream.open())
          throw special_exception;
      A_EXCEPTION_HANDLED:
//...
//...
}
catch(special_exception ex)
{
  // trying to fix the error here
  if(error_is_fixed)
     goto A_EXCEPTION_HANDLED;
  // else clean up and show error message
};

这个例子在一个嵌套循环中,我需要确保抛出之后的代码执行正确,所以我必须写大量的开销来再次启动迭代,在迭代中抛出了异常。这种情况会被认为是对goto的合理使用,还是像跳出循环等一样糟糕?

我认为根据您的描述,代码可以这样重写:

for ( /* usual stuff */ )
    try{
          if(!ifstream.open())
              throw special_exception;
    } catch (const special_exception&) {
        if (/* "can't handle the truth!" */) {
            // clean up & show error message
            break;
        }
    }
    // OK, continue
}

在异常后重试的概念并非闻所未闻(例如:Common Lisp将类似的东西作为语言的一部分来实现)。

然而,你真的需要一个例外吗?如果你(与库相反)抛出异常,并且只有一个地方抛出它(正如你的例子所示),你就不能简单地写这样的东西吗?

if(!ifstream.open()) {
    // trying to fix the error here
    if(!error_is_fixed) {
        throw really_unrecoverable();
    }
    // else let it continue normally
}

如果这种逻辑重复得太频繁,我可以看到异常的正当性,并使用goto重试算法。但是,如果这种情况只发生一两次,我会以内联方式编写恢复代码(也许使用辅助函数)。

使用goto实现循环不是对goto的合理使用。

要在C++中实现循环,请使用循环构造。

关于您具体案例的细节,请务必提供。

在您给出的示例中,我认为使用goto遍历两个块确实是一种糟糕的风格。

现在"goto"的使用会产生各种不受欢迎的歇斯底里,但在这种情况下,它糟糕风格的原因非常清楚:

代码的读者可能会合理地认为,一旦调用了异常处理程序,就会发生一些非常糟糕的事情,阻止当前代码上下文继续运行。如果预期ifstream存在,但实际不存在,则这是一种例外情况,并且有必要进行例外处理。然而,如果打开流的尝试只是一个测试,可以合理地预期会失败,那么这并不是一件例外的事情。在这种情况下,根本不要使用异常。在这种情况下,表达意图的更好方式可能是这样的:

for(/* usual stuff */)
    while (!ifstream.open()) {
      if (!attemptRemedialAction()) {
        throw really_bad_thing_happened_exception;
      }
    }
  // do things with ifstream, 
  // including goto statements if they express your intent elegantly
}
// allow the outer context or the caller to deal with the exceptional case of non-recovery

风格正是它听起来的样子。有些人喜欢一个,另一些人喜欢另一个。作为一名开发人员,你将与许多人一起工作,并且必须处理他们不同的风格。

真正重要的不是宣扬一种风格而不是另一种风格,而是在项目中保持风格的一致性。如果这个项目使用goto,那么坚持使用它,并相应地编写您的部分。因为其他开发人员会期待跳跃,并会寻找它们。

(顺便说一句,有些系统在任何给定的时间都只允许存在一个异常,例如Symbian和其他EPOC衍生物,所以我们在这里看到的可能是必要的,而不是风格)