如果我在项目中禁用C++异常,会发生什么?

23

Visual C++有一个编译器设置"启用C++异常",可以设置为"否"。如果我这样设置会发生什么?我的代码从未显式地抛出或捕获异常(因此第一个抛出的异常将无论如何终止程序),也不依赖于堆栈展开 - 我应该期望重新编译后的程序有任何不希望的行为吗?


1
我猜你想要提高程序性能。你试过了吗?禁用程序中的异常会有多大帮助? - Eamon Nerbonne
Visual C++是否支持WG21宏:__cpp_exceptions - user2023370
7个回答

17

MSDN文档中有这个选项的解释,它解释了不同的异常模式,并给出了代码示例以展示不同模式之间的区别。此外,这篇文章也可能很有趣尽管它比较老。

简而言之: 此选项基本上启用或禁用跟踪所有对象的生命周期。这种跟踪是必需的,因为在发生异常时,需要调用所有正确的析构函数、展开栈并执行大量的清理工作。这种跟踪需要组织开销(即额外的代码),可以通过将选项设置为“否”来删除该开销。

我自己没有尝试过,但如果将选项设置为“否”,看起来仍然可以throwcatch异常,但是缺少清理和展开,这可能会产生非常糟糕的后果(不建议使用;) ..


3
你肯定可以继续投掷和接球,但需要自己管理生命周期。如果模块大小是个问题,那么这种方法可以让你获得相当大的优势。 - 1800 INFORMATION

9
编译器会在抛出异常后,忽略析构函数及其他清理C++对象的栈展开代码。换句话说,它省略了一些清理代码。这样做可以显著提高性能,但如果确实抛出异常,则会导致严重问题。(如果你不相信,请自己计算时间。)除非在某些必须禁用异常的关键应用程序中,否则性能差异并不是禁用异常的真正原因。

8
跟随这个链接并亲自查看。如果你不相信,那就自己计时。我已经在我的平台上做过了。这是有成本的。 - Crashworks
但是由于我的代码中没有try-catch,第一个抛出的异常将会导致程序终止。我已经看不到比现在更糟糕的情况了。 - sharptooth
4
它可以减小代码体积,提高缓存性能,并消除许多分支,还可以提高性能。 - Adam Rosenfield
1
不要假设在没有异常支持的程序中抛出异常只会优雅地终止程序。根据机制的实现方式,如果发生抛出并且RTL不包括异常支持,则会出现不可预测的行为。考虑执行旧式的longjmp,传递一个未使用先前setjmp初始化的变量。 - Fabio Ceconello
2
据我所知,性能差异的主要原因是编译器在启用异常处理时必须关闭一些优化。也就是说,它不能重新排列指令,使得当异常从子例程中冒泡时,对象部分构造完成。 - Die in Sente

3
当您拒绝“启用C++异常”时,编译器不会选择同步的异常处理模型(/GX或/EHsc)。在此模式下,不会启用解旋语义。也就是说,在执行throw操作的函数和捕获throw操作的函数之间的帧上自动存储的对象将不会被销毁。
您可以参考MSDN或Visual C++异常常见问题以获取更多关于异常处理的细节。
class Test
{
public:

    Test()
    {
        printf("Test::constructor");
    }
    ~Test()
    {
        printf("Test::Destructor");
    }

};


int _tmain(int argc, _TCHAR* argv[])
{
    int a;

 try
   {
     Test a;
     int* p = 0;
     *p = 0; // Cause access violation
   }
   catch (...)
   {
      printf("Caught access violation"); 
   }
    return 0;
}

如果不进行异常处理,输出结果将会是:

Test::constructor
Caught access violation

“NO” 是 MSVC 的默认设置吗?GCC 和 Clang 默认开启,可以通过标志关闭(即 -fno-exceptions)。 - user2023370

3
你仍然可以通过使用 __try、__except 和 __finally 来处理结构化异常处理(SEH)。
C++ 异常处理只是在 SEH 基础上构建的类实现。
如果你试图在具有 SEH 异常处理程序的函数中实例化类(即对象需要解开引用),编译器也会发出警告,这可能会有点麻烦,但有方法可以解决。

3

我曾经处理的代码总是关闭异常。我没有看到过损坏或资源浪费的问题。

我认为,在C++中,异常通常不太适合。由于缺乏GC,堆栈爆炸使整个异常处理变得非常麻烦。此外,还有一个关于“异常”真正意义的争论,与可能的失败相比较。


1
就标准 C++ 而言,你将会得到未定义的行为。C++ 标准不允许全局关闭异常的可能性,并且某些标准库操作在某些情况下被定义为抛出异常。

好的,但是禁用C++异常的合法原因是什么呢?看起来这只是一种导致未定义行为的直接方式。 - sharptooth
2
我不相信有正当的理由关闭异常 - 关闭异常就像关闭整数一样没有意义。 - anon
禁用C++异常使得在内核模式下可以使用C++的受限子集。 - bk1e
3
如果关闭异常处理,那么其他许多 C++ 特性(如构造函数、模板)就几乎无法使用了,这时候你可能会想干脆用 C 语言。至少 C 是一种标准化的语言,而“C++ 子集”并不是。 - anon
据我所知,在 C++ 的自由实现中,是否支持异常处理是由具体实现定义的。 - Johannes Schaub - litb
我查了一下:它看起来像是在独立的实现中也需要工作。不确定我在哪儿读到它们是可选的。 - Johannes Schaub - litb

0
如果operator new无法分配内存,我相信它会返回NULL而不是抛出std::bad_alloc异常。
如果您调用任何抛出异常的第三方库,将会产生很多问题。

我预计这不会比我已经遇到的问题更糟糕。由于我没有捕获任何异常,任何地方抛出的异常都会立即导致程序终止。我有什么遗漏吗? - sharptooth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接