【问题标题】:Does it make sense to catch exceptions in the main(...)?在 main(...) 中捕获异常是否有意义?
【发布时间】:2010-09-26 23:02:26
【问题描述】:

我在一个项目中发现了一些看起来像这样的代码:

int main(int argc, char *argv[])
{
  // some stuff

 try {
  theApp.Run();
 } catch (std::exception& exc) {
  cerr << exc.what() << std::endl;
  exit(EXIT_FAILURE);
 }
 return (EXIT_SUCCESS);
}

我不明白为什么要捕获异常。如果不是,应用程序将简单地退出并打印异常。

您认为有什么好的理由在这里捕获异常吗?


编辑:我同意打印异常错误是件好事。但是,重新抛出异常不是更好吗?我觉得我们在这里吞下了它……

【问题讨论】:

  • 怎么会被吞下去?代码打印异常消息,然后退出。你还能做什么?如果你重新抛出异常,谁会看到它?会在哪里抓到?毕竟这是程序的入口点。

标签: c++ exception exception-handling


【解决方案1】:

如果一个异常未被捕获,那么标准没有定义堆栈是否被展开。所以在某些平台上会调用析构函数,而在其他平台上程序会立即终止。在顶层捕获可确保始终调用析构函数。

因此,如果您没有在调试器下运行,那么捕获所有内容可能是明智之举:(...) 以及 std::exception。然后,即使出现致命异常,您的应用程序代码也可以使用 RAII 进行清理。在许多此类情况下,您实际上不需要清理,因为操作系统会为您完成。但例如,您可能更愿意在可能的情况下完全断开与远程服务的连接,并且可能存在进程外部的资源,例如命名管道/互斥体,您更愿意销毁而不是泄漏。

在我看来,在 main 中重新抛出异常的用处有限,因为您已经丢失了最初抛出异常的上下文。我认为在调试器中捕获未捕获的异常比仅将故障记录到 std::cerr 更嘈杂,因此如果有可能丢失日志记录,重新抛出将是明智之举。

如果您希望调试器在调试模式下捕获意外情况,在发布模式下会引发异常并最终导致退出,那么除了不捕获异常以便调试器看到它​​之外,还有其他方法可以做到这一点。例如,您可以使用断言宏。当然,如果您在 .NET 上使用 SEH,这对于意外和不可预测的情况(例如硬件异常)无济于事。

【讨论】:

    【解决方案2】:

    为什么说会打印异常?这不是 C++ 运行时的典型行为。充其量,您可以期望它的类型被打印出来。

    此外,此程序会留下“失败”状态,而异常可能会导致终止中止状态(即退出代码中指示的信号)。

    【讨论】:

    • 你说得对,我误以为会打印异常。事实并非如此。
    【解决方案3】:

    主函数中的 Try-catch 对调试器隐藏了异常。我会说,这不好。

    另一方面,客户不应该有调试器,所以捕捉异常是件好事。所以很好。

    就我个人而言,我在构建发布版本时会捕获 main 函数中的所有异常,而在构建调试配置时我不会这样做。

    【讨论】:

      【解决方案4】:

      堆栈未展开情况的简单示例:
      Why destructor is not called on exception?

      异常可能导致应用程序终止而不是展开堆栈的情况列表。
      Why destructor is not called on exception?

      如果在任何级别都没有捕获到异常并且会逃逸 main(),则允许实现调用 terminate() 而不是展开堆栈(是的,这也让我感到意外)。

      因此,我总是在 main() 中捕获所有异常。

      int main()
      {
          try
          {
          }
          catch(std::exception const& e)
          {  /* LOG */
             // optimally rethrow
          }
          catch(...) // Catch anything else.
          {  /* LOG */
             // optimally rethrow
          }
      }
      

      帮助在调试过程中发现问题。从 std::exception 派生异常,然后在 std::exception 的构造函数中粘贴断点。

      【讨论】:

        【解决方案5】:

        这是一个全局 catch 块。显示一个很好且用户理解的消息(“内部错误”)而不是神秘的异常打印输出是很常见的。这可能从特定的代码块中看不出来,但总的来说是个好主意。

        【讨论】:

        • 这只是一个好主意,直​​到你第一次必须调试一些神秘的异常,这是从地狱抛出的知道在哪里...... :)
        • 那我们不能打印然后重新扔吗?
        • 我想我们可以,但是当我调试时 - 我很少看打印的内容 - 我通常会查看监视窗口中的值(如果需要)。
        【解决方案6】:

        看看 C++ 圣经,即 Stroustrup,他有一个例子,在 Applied C++ 编程中也有重复。理由是:

        int main(void)
        {
             try
             {
                  // your code 
             }
             catch ( /* YourPossibleExceptions i.e. barfs you expect may occur */ )
             {
             }
             catch ( ... ) // unexpected errors, so you can exit gracefully
             {
             }
        }
        

        【讨论】:

        • 是的。但正如我在回答中提到的那样 - 它并没有真正帮助调试器。如果未捕获到异常 - 调试器将向您显示源中引发异常的确切位置。因此,这仅在发布版本中才有意义。
        【解决方案7】:

        根据 Windows 规范,main 不允许抛出。 (实际上它会导致询问您是否要将错误报告发送给 Microsoft 的消息)

        【讨论】:

        • 您能否链接到它所说的 Windows 规范?这将为您的答案提供一些额外的背景/权威。
        猜你喜欢
        • 2023-03-14
        • 1970-01-01
        • 2011-02-12
        • 1970-01-01
        • 1970-01-01
        • 2014-07-15
        • 2019-03-12
        • 2014-04-30
        • 2012-05-22
        相关资源
        最近更新 更多