使用catch(...)(省略号)进行事后分析

时间:2010-02-02 09:55:02

标签: c++ exception-handling debugging try-catch ellipsis

有人在另一个问题建议使用catch(...)通过将整个main()try{}catch(...){}块一起包围来捕获所有其他未处理的 - 意外/未预见的异常。

这听起来像一个有趣的想法,可以节省大量时间调试程序,并至少留下一些发生的事情。

问题的实质是哪些信息可以恢复那种方式(除了我留下的任何调试全局),以及如何来恢复它(如何访问)并且识别所谓的任何捕获物)

此外,有什么警告与它有关。特别是:

  • 它会在稍后萌芽的线程中发挥出色吗?
  • 它不会破坏处理段错误(在其他地方作为信号捕获)
  • 它不会影响其他try ... catch块不可避免地嵌套在里面,那是否有处理预期的异常?

6 个答案:

答案 0 :(得分:10)

是的,这是一个好主意。

如果你让异常转义为main,那么在实现定义的天气之前,堆栈会在应用程序关闭之前解开。所以在我看来,你必须抓住主要的所有例外情况。

然后问题就变成了如何处理它们 一些操作系统(参见MS和SE)提供了一些额外的调试工具,因此在捕获异常之后重新抛出异常非常有用(因为无论如何堆栈现在已经解开)。

int main()
{
    try
    {
        /// All real code
    }
    // I see little point in catching other exceptions at this point 
    // (apart from better logging maybe). If the exception could have been caught
    // and fixed you should have done it before here.

    catch(std::exception const& e)
    {
         // Log e.what() Slightly better error message than ...
         throw;
    }
    catch(...)   // Catch all exceptions. Force the stack to unwind correctly.
    {
        // You may want to log something it seems polite.
        throw;  // Re-throw the exception so OS gives you a debug opportunity.
    }
}
  
      
  • 它会在稍后萌芽的线程中发挥出色吗?
  •   

它应该对线程没有影响。通常,您必须手动加入任何子线程以确保它们已退出。当主要出口没有明确定义时,子线程会发生什么事情的确切细节(因此请阅读您的文档)但通常所有子线程都会立即死亡(一种令人讨厌和可怕的死亡,不涉及展开它们的堆栈)。

如果您正在讨论子线程中的异常。同样,这个定义不明确(所以请阅读您的文档)但是如果一个线程通过异常退出(即由于异常而不是返回而用于启动线程的函数退出)那么这通常会导致应用程序终止(同样的影响)如上)。因此,最好停止所有例外退出线程。

  
      
  • 它不会破坏处理段错误(在其他地方作为信号捕获)
  •   

信号不受异常处理机制的影响 但是因为信号处理程序可能会在堆栈上放置一个奇怪的结构(因为它们自己的返回处理回到正常代码),所以从信号处理程序中抛出异常不是一个好主意,因为这可能会导致意外结果(并且绝对不可移植) )。

  
      
  • 它不会影响其他try ... catch块不可避免地嵌套在里面,那是否有处理预期的异常?
  •   

应该对其他处理程序没有影响。

答案 1 :(得分:4)

据我所知,Win32上的catch(...)还会收到SEH异常,而不希望执行此操作。如果你得到SEH例外,那是因为发生了非常可怕的事情(主要是访问违规),所以你不能再相信你的环境了。您可以做的几乎所有事情都可能因另一个SEH例外而失败,因此它甚至不值得尝试。此外,一些SEH例外旨在被系统捕获;更多关于此here

所以,我的建议是对所有异常使用基本异常类(例如std::exception),并在“catchall”中捕获该类型;你的代码不能准备好处理其他类型的异常,因为它们根据定义是未知的。

答案 2 :(得分:3)

全局try catch块对生产系统很有用,以避免向用户显示令人讨厌的消息。在开发过程中,我认为最好避免使用。

关于你的问题:

  • 我认为全局catch块不会捕获另一个线程中的异常。每个线程都有自己的堆栈空间。
  • 我不确定。
  • 嵌套的try ... catch块不受影响,将照常执行。异常在堆栈中向上传播,直到找到try块。

答案 3 :(得分:1)

如果您正在制作.net应用程序,可以尝试a solution I use。这捕获了所有未处理的异常。我通常只在不使用调试器时为生产代码启用代码(#ifndef DEBUG)。

值得指出的是,kgiannakakis提到您无法在其他线程中捕获异常,但您可以在这些线程中使用相同的try-catch方案并将异常发布回主线程,您可以在其中重新抛出它们获得错误的完整堆栈跟踪。

答案 4 :(得分:0)

  

以及如何恢复它(如何访问   并认识到任何捕获   叫())

如果您的意思是如何恢复抛出的异常类型,您可以在回退到catch (...)之前链接特定类型的catch块(从更具体到更一般):

try {
   ...
} catch (const SomeCustomException& e) {
   ...
} catch (const std::bad_alloc& e) {
   ...
} catch (const std::runtime_error& e) {
   // Show some diagnosic for generic runtime errors...
} catch (const std::exception& e) {
   // Show some diagnosic for any other unhandled std::exceptions...
} catch (...) {
   // Fallback for unknown errors.
   // Possibly rethrow or omit this if you think the OS can do something with it.
}

请注意,如果您发现自己在多个地方执行此操作并希望合并代码(可能需要多个main函数用于单独的程序),您可以编写一个函数:

void MyExceptionHandler() {
   try {
      throw; // Rethrow the last exception.
   } catch (const SomeCustomException& e) {
      ...
   }
   ...
}

int main(int argc, char** argv) {
   try {
      ...
   } catch (...) {
      MyExceptionHandler();
   }
}

答案 5 :(得分:0)

catch-all不会非常有用,因为没有可以查询的类型/对象信息。但是,如果可以确保应用程序引发的所有异常都是从单个基础对象派生的,则可以使用catch块作为基本异常。但那不是一个全能的。