禁用C++异常,如何让std :: throw()立即终止?

uni*_*n83 34 c++ exception-handling g++ exit

这个C++程序是一个CGI脚本,我不想处理异常.我宁愿获得边际性能提升,让操作系统(Linux)在进程终止后处理清理.

我正在使用标准C++库,并希望die在Perl中使用任何函数:每当它抛出异常时.没有展开,在我的过程中运行任何进一步的代码.

-fno-exceptions如何工作?如果我的代码中根本没有捕获,并且基本上假装不存在异常.但我确实使用std :: c ++库,可以 throw()?

bdo*_*lan 66

选项#1:简单地从不捕获异常.

当他们没有被抛出或被抓住时,例外没有太大的开销; 如果你正在投掷并且没有准备好赶上,那么你无论如何都要死,所以那时的表现影响是微不足道的.另请注意,如果未处理异常,则不会执行堆栈展开; 程序将直接终止而不执行堆栈展开.

值得注意的是,在G ++中,异常几乎没有实际抛出的开销.G ++生成足够的额外信息以通过堆栈追溯程序的执行,以及一些额外的代码来调用析构函数等 - 但是在实际抛出异常之前,不会使用这些额外的代码或数据.因此,您不应该看到启用了异常但未使用的代码与禁用异常的代码(通过任何机制)之间的性能差异.

选项#2:通过-fno-exceptions.

这个标志指示G ++ 做两件事:

  1. 删除STL库中的所有异常处理; 投掷被abort()电话取代
  2. 堆栈展开数据和代码被删除.这节省了一些代码空间,并且可能使编译器的寄存器分配更容易(但我怀疑它会对性能产生很大的影响).但是,值得注意的是,如果抛出异常,并且库试图通过-fno-exceptions代码展开,它将在该点中止,因为没有展开数据.

这将有效地将所有异常转换为abort()s,如您所愿.但请注意,您不会被允许throw- 代码中的任何实际throws或catchs都将导致编译时错误.

选项#3 :(不可移植,不推荐!)挂钩__cxa_allocate_exception.

使用(其中包括)__cxa_allocate_exception和__cxa_throw内部库函数来实现C++异常.您可以实现将这些函数挂接到abort()的LD_PRELOAD库:

void __cxa_allocate_exception() { abort(); }
void __cxa_throw() { abort(); }
Run Code Online (Sandbox Code Playgroud)

警告:这是一个可怕的黑客攻击.它应该适用于x86和x86-64,但我强烈建议不要这样做.值得注意的是,它实际上不会提高性能或节省代码空间-fno-exceptions.但是,它将允许throw 语法,同时将throws转换为abort()s.

  • @Steve,是的,我的所有建议都是G ++特有的.毕竟,标准没有指定_any_方法来完全中性异常:) (3认同)
  • +1; 只是不要捕捉异常。这是他们在那里的原因之一。 (2认同)
  • "另请注意,如果未处理异常,将不会执行堆栈展开;程序将在不执行堆栈展开的情况下终止." - 标准不保证这一点.允许实现展开堆栈(15.5.1/2).所以对g ++来说没关系,但是如果代码需要全部可移植的话要小心. (2认同)
  • 当然,我提到它只是因为它是你提供的一个选项,乍一看可能看起来好像它适用于其他编译器(并且可能是,只要你检查他们的文档 - 行为是实现定义的,所以它必须记录在案). (2认同)

GMa*_*ckG 19

-fno-exceptions将所有标准库throw转换为调用std::abort().它处理你不能直接修改的部分,其余的是在代码中根本不使用它们.

当然,我真的怀疑你这样做的理由.当你实际投掷时,你只会"失去"表现,并且你会丢掉一个重要且有用的语言.

  • @Andre:那是你自己的try-catch块吗?您不能禁用任何异常. (3认同)
  • 你“不仅仅在实际投掷时“失去”表现”。任何可能需要展开的函数都可能会变慢。 (2认同)

Fre*_*pin 11

万一有人偶然发现这个问题,我想纠正@GManNickG和(/sf/answers/507462231/)和@bdonlan(/sf/answers/507460971/))在答案中说.不幸的是,关于"-fno-exception"删除所有异常处理代码并将所有抛出中止的部分都是错误的.好吧 - 部分错了.当您使用此标志编译有问题的库(libstdc ++ v3)时,这是正确的,但如果您在使用此标志编译的自己的代码中使用此库(作为.a或.so或.dll或其他),则为true .在后一种情况下,您的代码中的异常处理代码是被禁止的,但是库内的所有异常处理调用仍然存在(因为库是在没有此标志的情况下编译的,启用了异常),所以如果您使用new那么您的可执行文件具有异常处理代码 - 唯一的区别是你不能对这些异常做任何事情catch()(在你的代码中被禁止),所以所有抛出都会有效地结束abort(),但只是因为没有人抓住它们.


Ed *_*eal 9

引用:

这个C++程序是一个CGI脚本,我不想处理异常.

  • 然后不要.简单.异常将很快到达堆栈顶部.

但我会敦促你这样做.这样做意味着你正在考虑可能出错的事情.