C ++捕获所有异常


242

是否有Java的C ++等价物

try {
    ...
}
catch (Throwable t) {
    ...
}

我正在尝试调试调用本机Windows函数的Java / jni代码,并且虚拟机不断崩溃。本机代码在单元测试中看起来不错,并且仅在通过jni调用时似乎崩溃。通用的异常捕获机制将被证明非常有用。



2
请注意,大多数崩溃不是由C ++中的异常引起的。您可以捕获所有异常,但这不会阻止许多崩溃。
Mooing Duck 2016年

Answers:


333
try{
    // ...
} catch (...) {
    // ...
}

将捕获所有C ++异常,但应将其视为错误的设计。您可以使用c ++ 11的新current_exception机制,但是如果您无法使用c ++ 11(需要重写的旧版代码系统),则您没有命名的异常指针可用于获取消息或名称。您可能想为可以捕获的各种异常添加单独的catch子句,并且仅捕获底部的所有内容以记录意外的异常。例如:

try{
    // ...
} catch (const std::exception& ex) {
    // ...
} catch (const std::string& ex) {
    // ...
} catch (...) {
    // ...
}

68
通过const引用捕获异常是一个好习惯。如:catch(std :: exception const&ex){/ * ... * /}
coryan

12
@coryan:为什么按const引用捕获是一种好习惯?
Tim MB

19
避免不必要的复制是一项好处。
Greg D

21
-1:关于将“捕获C ++中的所有异常”的建议具有误导性。尝试在try块内生成除以零的错误。您将看到它将生成未捕获的异常,但是代码显然是在C ++中。声明这将“捕获所有C ++异常”,然后在有关有用性有限的说明中添加一些结构化异常的提及,将更为有帮助。
omatai

42
@omatai:已修复,它将捕获所有C ++异常。被零除是未定义的行为,并且不会生成C ++异常。
Mooing Duck 2013年

150

有人应该补充说,在C ++代码中无法捕捉“崩溃”。那些不会抛出异常,但是会做任何他们喜欢的事情。当您看到由于空指针取消引用而导致程序崩溃时,它正在执行未定义的行为。没有std::null_pointer_exception。尝试捕获异常将无济于事。

仅在某人正在读取此线程并认为他可以获取导致程序崩溃的原因的情况下。应该改用gdb之类的调试器。


4
好吧,正如Shy指出的那样,使用VC编译器是可能的。这不是一个好主意,但是有可能。
Shog9

7
是的,SEH。但不能使用健全的标准c ++技术:)好吧,如果您坚持使用Windows,几乎可以完成所有操作:)
Johannes Schaub-litb

1
嗯...谢谢你的花絮。我一直在寻找为什么我的空指针异常没有被捕获的答案!
大林·塞维赖特09年

10
您可以在Windows上使用SEH并在POSIX系统上使用signal(2)/ sigaction(2)捕获段错误,它涵盖了当今使用的绝大多数系统,但是像异常处理一样,它不应该用于常规流控制。它更像是“在死亡之前做一些有用的事情”。
亚当·罗森菲尔德2009年

1
@AdamRosenfield,直到您实现try { .. } catch(...) { ... }了使用信号/信号捕获的实现,我都不会称其为“捕获” :)如果在信号处理程序中,程序员相对很难知道崩溃在代码中的何处发生(我在说)关于以编程方式检测到的内容),与try / catch相比。
Johannes Schaub-litb 2014年

72

这是您可以在catch(...)需要时使用GCC 从内部对异常类型进行逆向工程的方式(在从第三方库中捕获未知信息时可能很有用):

#include <iostream>

#include <exception>
#include <typeinfo>
#include <stdexcept>

int main()
{
    try {
        throw ...; // throw something
    }
    catch(...)
    {
        std::exception_ptr p = std::current_exception();
        std::clog <<(p ? p.__cxa_exception_type()->name() : "null") << std::endl;
    }
    return 1;
}

如果您负担得起使用Boost的费用,则可以使捕获部分更简单(在外部)并可能跨平台

catch (...)
{
    std::clog << boost::current_exception_diagnostic_information() << std::endl;
}

58
try {
   // ...
} catch (...) {
   // ...
}

注意...里面的catch是一个真实的省略号,即。三个点。

但是,由于C ++异常不一定是基Exception类的子类,因此没有任何方法可以实际看到使用此构造时抛出的异常变量。


24
在C ++ 11中,有:try {std :: string()。at(1); //这会生成一个std :: out_of_range} catch(...){eptr = std :: current_exception(); //捕获}
Mohammad Alaggan

2
@bfontaine:是的,但是我说过,要catch在注释(// ...)中将说明符与现有代码占位符区分开,这显然不是C ++语法。
Greg Hewgill 2014年

1
@GregHewgill:是的,这只是印刷的挑剔。
bfontaine 2014年

1
@bfontaine:足够公平。:)
Greg Hewgill 2014年

43

(在C ++中)不可能以可移植的方式捕获所有异常。这是因为某些异常不是C ++上下文中的异常。这包括零错误除以除法和其他方法。发生这些错误时,可能会黑客入侵并因此而引发异常,但是这样做并不容易,而且肯定也不容易以便携式方式正确处理。

如果要捕获所有STL异常,可以执行

try { ... } catch( const std::exception &e) { ... }

这将允许您使用e.what(),这将返回const char*,可以告诉您有关异常本身的更多信息。这是您最想问的类似于Java构造的构造。

如果某人足够愚蠢以引发不继承自的异常,这将对您没有帮助std::exception


1
为什么不在顶部?
伊万·桑兹·卡拉萨

30

简而言之,使用catch(...)。但是,请注意,catch(...)它应与以下内容结合使用throw;

try{
    foo = new Foo;
    bar = new Bar;
}
catch(...)       // will catch all possible errors thrown. 
{ 
    delete foo;
    delete bar;
    throw;       // throw the same error again to be handled somewhere else
}

这是正确的使用方法catch(...)


6
最好使用RAII进行自动处理此异常情况的内存管理。
paykoob 2014年

1
@paykoob如何处理您试图创建一个新foo但在酒吧失败的情况。或当bar的构造函数尝试打开文件但失败并因此引发时。那么您可能最终会得到一个dangeling foo
Mellester,2014年

2
@MelleSterk在这种情况下,是否仍要清理堆栈,该堆栈将运行Foo的析构函数?我认为这就是RAII的重点。但是,如果您需要指向a的指针Foo而不是仅Foo在堆栈上创建,则需要将指针包装在堆栈上声明的其他内容中。
reirab

是auto foo = std :: make_unique <Foo>(); 自动条= std :: make_unique <Bar>(); //是异常安全的,不会泄漏,不需要catch(...)
paulm 2015年

如果仅是针对开始的讨论,则此答案值得投票:)
Cristik 2015年

21

可以这样写:

try
{
  //.......
}
catch(...) // <<- catch all
{
  //.......
}

但是这里有一个非常不明显的风险:您无法找到已在try块中引发的错误的确切类型,因此catch在确保无论异常类型是什么时,程序都必须持久化时,请使用这种类型的错误。按照catch块中定义的方式。


31
希望您能在提供优质答案后将近5年的时间里回答问题,从而获得某种徽章!


18

您可以使用

catch(...)

但这很危险。约翰·罗宾斯(John Robbins)在他的《调试Windows》一书中讲述了一个战争故事,内容涉及一个由catch(...)命令掩盖的非常讨厌的错误。捕获特定的异常要好得多。捕获任何您认为您的try块可能会合理抛出的东西,但是如果确实发生某些意外情况,请让代码向上抛出一个异常。


1
我只是了解了这些用法,并在那个阶段添加了一些日志。毫无例外地无所事事肯定是自找麻烦。
jxramos

15

让我在这里提一下:Java

try 
{
...
}
catch (Exception e)
{
...
}

可能无法捕获所有异常!实际上,我以前曾经发生过这种事情,这很令人发指。异常源于Throwable。因此,从字面上看,要捕获所有内容,您就不想捕获异常。您想抓住Throwable。

我知道这听起来很挑剔,但是当您花了几天的时间试图找出“未捕获的异常”来自哪里时,该代码被try ... catch(Exception e)块所包围,它坚持您。


2
当然,您永远不要捕获Error对象-如果您应该捕获它们,它们将是Exceptions。错误对象完全是致命的事情,例如堆空间
用尽

1
多数情况下都不是运行时异常GoodProgrammerExpected异常!!!
OscarRyz

3
我们遇到了一个非常严重的错误,原因是由于catch(Throwable)块而不是让它杀死东西而导致捕获OutOfMemoryError ...
Trejkaz 2012年

1
当然,catch(Exception)可能无法捕获Java中的所有异常,但是您会将其与C#混合在一起... Java = catch(Thowable),C#= catch(Exception)。不要让他们感到困惑。
法老王大厨2013年

2
@OscarRyz听起来像CoderMalfunctionError(实际上是一个真正的Java Error子类……虽然并不意味着听起来像什么。)
reirab 2015年

9

好吧,如果您想捕获所有异常以创建一个小型转储,例如...

有人在Windows上完成了工作。

请参阅http://www.codeproject.com/Articles/207464/Exception-Handling-in-Visual-Cplusplus 在本文中,他解释了如何找到各种异常,并提供了有效的代码。

这是您可以捕获的列表:

 SEH exception
 terminate
 unexpected
 pure virtual method call
 invalid parameter
 new operator fault 
 SIGABR
 SIGFPE
 SIGILL
 SIGINT
 SIGSEGV
 SIGTERM
 Raised exception
C++ typed exception

和用法:CCrashHandler ch; ch.SetProcessExceptionHandlers(); //对一个线程执行此操作ch.SetThreadExceptionHandlers(); //每一个


默认情况下,这会在当前目录(crashdump.dmp)中创建一个小型转储。


4

通用的异常捕获机制将被证明非常有用。

疑。您已经知道您的代码已损坏,因为它崩溃了。饮食异常可能掩盖了这一点,但这可能只会导致甚至更原始,更微妙的错误。

您真正想要的是调试器...


13
我不同意,在实时应用程序中有很多情况,我宁愿捕获未知的异常,在日志中写入任何内容 /采取一些常规的错误措施,而不是使应用程序崩溃。
2011年

3
我怀疑您是在考虑可以采取一些一般性错误措施的情况,而忽略那些浪费堆栈或内存已用完并且一般性错误处理也不会成功的情况。可以从中恢复的捕获错误没什么问题,但是恕我直言,“全部捕获”实际上仅应以隔离(单独的堆栈,预分配的内存),在程序终止之前调用的精心编写的逻辑存在。如果您不知道问题出在哪里,则无法确定可以从中解决问题。
Shog9 2011年

1
即安装一个信号处理程序,该信号处理程序将消除您在运行时生成的一些日志,以找出程序崩溃的位置以及希望的原因。
2014年

3
  1. 您是否可以从控制台窗口(从Java命令行启动)运行使用JNI的Java应用程序,以查看是否有关于JVM崩溃之前可能检测到的报告。直接作为Java窗口应用程序运行时,如果您从控制台窗口运行,则可能会丢失显示的消息。

  2. 其次,您是否可以对JNI DLL实现进行存根处理,以显示DLL中的方法是从JNI输入的,您是否正确返回等?

  3. 万一问题出在C ++代码中不正确使用JNI接口方法之一的情况下,您是否已验证一些简单的JNI示例可以编译并使用您的设置?我特别在考虑使用JNI接口方法将参数转换为本地C ++格式并将函数结果转换为Java类型。最好对它们进行存根,以确保数据转换有效,并且您不会在类似于COM的调用中进入JNI接口。

  4. 还有其他事情需要检查,但是很难在不了解您的本机Java方法是什么以及它们的JNI实现正在尝试做什么的情况下提出任何建议。目前尚不清楚从C ++代码级别捕获异常是否与您的问题有关。(您可以使用JNI接口将异常作为Java重新抛出,但是从您提供的内容中尚不清楚这是否会有所帮助。)


2

对于无法正确调试使用JNI的程序的实际问题(或在调试器下运行该错误不会出现):

在这种情况下,通常有助于在您的JNI调用周围添加Java包装器(即,所有本机方法都是私有的,而您的类中的公共方法则对其进行调用),这些包装器会进行一些基本的健全性检查(检查所有“对象”是否已释放并且“对象”是否已释放) (在释放后不使用)或同步(仅将所有方法从一个DLL同步到单个对象实例)。让Java包装器方法记录错误并引发异常。

这通常比尝试在一个程序库中调试大型并行Java程序更容易找到真正的错误(这出乎意料的是,该错误主要是在Java代码中不遵循被调用函数的语义,从而导致某些令人讨厌的double-frees或类似错误)。本机调试器...

如果您知道原因,则将代码保留在避免该问题的包装方法中。最好让包装器方法引发异常,而不是让JNI代码使VM崩溃。


1

好吧,这实际上取决于编译器环境。gcc不能捕获这些。Visual Studio和我使用的最后一个Borland做到了。

因此,关于崩溃的结论是,它取决于开发环境的质量。

C ++规范说catch(...)必须捕获任何异常,但并非在所有情况下都可以。

至少从我尝试过的。


0

意识到

try{
// ...
} catch (...) {
// ...
}

仅捕获语言级别的异常,其他低级别的异常/错误,例如Access ViolationSegmentation Fault不会被捕获。


分段错误之类的东西实际上并不是例外,它们是信号。因此,您无法像典型的例外一样捕获它们。不过,也有像一些变通
MAChitgarha
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.