【问题标题】:Is using 'goto' in exception handlers bad style? [duplicate]在异常处理程序中使用“goto”是不好的风格吗? [复制]
【发布时间】:2014-04-23 13:57:11
【问题描述】:

自从我开始编程以来,我被教导如果可以避免的话,永远不要使用“goto”。然而,我遇到了一个案例,我认为 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
};

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

【问题讨论】:

  • 为什么“尝试修复错误”部分不能进入if(!ifstream.open()) 的正文? throw 如果修复失败,否则继续正常控制流。
  • 使用goto 跳转到后向标签通常是一种不好的风格(更不用说你应该避免它)。
  • 我猜你需要躺下再看问题。这里不需要goto

标签: c++ exception exception-handling goto


【解决方案1】:

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

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
}

【讨论】:

  • 这和goto有什么不同?
  • @JamesKanze 除了...不是goto?我不知道。我猜得和我一样多,OP似乎对此很满意(给定了复选标记)。更多信息,需要更多信息。
  • 这会重复 try 块的初始化部分,不是吗? (假设不是简单的if(!ifstream.open())...
  • goto 是异常发明之前的遗留物。这是一个 100% 多余的关键字。只有一种情况可以接受使用goto,那就是打破嵌套循环。即使在这种特定情况下,也有其他更好的解决方案。程序员之间的普遍共识是“好的 goto 可能并不总是有害的,但它 100% 是多余的,所以无论如何都不要使用它”。如果您试图将goto 用于嵌套循环以外的任何其他目的,这可能意味着您的程序设计很混乱,需要重新制作。
  • 从这个例子中我们可以看出,goto 只是增加了混乱。代码现在更具可读性。
【解决方案2】:

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

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

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

如果这个逻辑重复得太频繁,我可以看到异常的理由并使用 goto 重试算法。但如果它只发生一次或两次,我会编写内联恢复的代码(可能使用辅助函数)。

【讨论】:

    【解决方案3】:

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

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

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

    【讨论】:

      【解决方案4】:

      在您给出的示例中,我认为使用 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
      

      【讨论】:

        【解决方案5】:

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

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

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

        【讨论】:

          猜你喜欢
          • 2010-10-11
          • 1970-01-01
          • 2019-02-22
          • 2010-12-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-01-29
          相关资源
          最近更新 更多