代码之家  ›  专栏  ›  技术社区  ›  SF.

使用catch(…)(省略号)进行尸检分析

  •  14
  • SF.  · 技术社区  · 15 年前

    有人在另一个问题中建议使用 catch(...) 通过包围整个 main() try{}catch(...){} 块。

    这听起来是一个有趣的想法,可以节省大量时间调试程序,并至少留下一个发生了什么的提示。

    问题的实质是 可以恢复哪些信息 这样(除了我留下的调试全局数据之外) 以及如何 恢复它(如何访问和识别调用的catch)

    此外,什么警告与之相关。特别地:

    • 以后发芽的线会起到很好的作用吗?
    • 它是否不会中断处理分段故障(作为信号捕获到其他地方)
    • 它会不会影响其他的try…catch块,这些块不可避免地嵌套在内部,它们可以处理预期的异常?
    6 回复  |  直到 14 年前
        1
  •  10
  •   Loki Astari    15 年前

    是的,这是个好主意。

    如果让一个异常从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块,这些块不可避免地嵌套在内部,它们可以处理预期的异常?

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

        2
  •  4
  •   Matteo Italia    14 年前

    据我所知, catch(...) 在win32捕获时也会出现SEH异常,而您 不想要 这样做。如果你得到一个SEH例外,那是因为发生了非常可怕的事情(主要是访问违规),所以你不能再信任你的环境了。几乎你所能做的每件事都可能因为另一个SEH例外而失败,所以这甚至不值得尝试。此外,系统还打算捕获一些SEH异常;关于这方面的更多信息 here .

    因此,我的建议是使用一个基本异常类(例如 std::exception )对于所有的异常,在“catch all”中只捕获该类型;您的代码不能准备处理其他类型的异常,因为它们在定义上是未知的。

        3
  •  3
  •   kgiannakakis    15 年前

    全局Try-Catch块对于生产系统很有用,以避免向用户显示不好的消息。在开发过程中,我认为最好避免这种情况。

    关于你的问题:

    • 我相信全局catch块不会在另一个线程中捕获异常。每个线程都有自己的堆栈空间。
    • 我对此不确定。
    • 嵌套的try…catch块不受影响,将照常执行。异常向上传播到堆栈,直到它找到一个try块。
        4
  •  1
  •   Community CDub    8 年前

    你可以试试 a solution I use 如果您正在制作.NET应用程序。捕获所有未处理的异常。我通常只启用代码 #ifndef DEBUG )当我不使用调试器时生成的代码。

    值得指出的是,正如Kgianakakis提到的,您不能在其他线程中捕获异常,但是您可以在这些线程中使用相同的Try-Catch方案,并将异常发回主线程,在主线程中您可以重新抛出异常,以获得错误的完整堆栈跟踪。

        5
  •  0
  •   dirkgently    15 年前

    由于没有可以查询的类型/对象信息,因此“全部捕获”不会非常有用。但是,如果你能确定 全部的 应用程序引发的异常是从单个基对象派生的,您可以为基异常使用catch块。但那不是什么好事。

        6
  •  0
  •   jamesdlin    15 年前

    以及如何恢复它(如何访问 认识到任何捕获 打电话来)

    如果您的意思是如何恢复抛出的异常类型,则可以在返回到之前为特定类型(从更具体到更一般)链接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();
       }
    }