/EH(异常处理模型)

指定编译器生成的异常处理模型支持。 参数指定是否将 catch(...) 语法应用于结构化和标准 C++ 异常,是否将 extern"C" 代码假定为 throw 异常,以及是否优化某些 noexcept 检查。

语法

/EHa[-]
/EHs[-]
/EHc[-]
/EHr[-]

自变量

a
启用标准 C++ 堆栈展开。 使用 catch(...) 语法时捕获结构化(异步)和标准 C++(同步)异常。 /EHa 将重写 /EHs/EHc 参数。

s
启用标准 C++ 堆栈展开。 使用 catch(...) 语法时仅捕获标准 C++ 异常。 除非另外指定 /EHc,否则编译器假定声明为 extern "C"的函数可能throw(引发)C++ 异常。

c
/EHs 一起使用时,编译器假定声明为 extern"C" 的函数绝不会throw(引发) C++ 异常。/EHa 一起使用时(即 /EHca 等同于 /EHa),没有效果。 如果未指定 /EHs/EHa,则忽略 /EHc

r
告知编译器始终为所有 noexcept 函数生成运行时终止检查。 默认情况下,如果编译器确定该函数仅调用non-throwing (非引发)函数,则运行时检查 noexcept 可能被优化掉。 此选项提供严格的 C++ 一致性,代价是一些额外的代码。 如果未指定 /EHs/EHa,则忽略 /EHr

-
清除上一个选项参数。 例如,/EHsc- 解释为 /EHs /EHc- 并且等效于 /EHs

/EH 参数可以单独指定,也可以以任何顺序组合。 如果指定了多个相同参数的实例,则最后一个将覆盖任何较早的实例。 例如, /EHr- /EHc /EHs/EHscr-相同,而 /EHscr- /EHr/EHscr 效果相同。

备注

默认异常处理行为

编译器始终生成支持异步结构化异常处理 (SEH) 的代码。 默认情况下(即,如果未指定 /EHsc/EHs/EHa 选项),编译器支持原生 C++ catch(...) 子句中的 SEH 处理程序。 但是,它也会生成仅部分支持 C++ 异常的代码。 默认的异常展开代码不会破坏因异常而超出范围的 try 块之外的自动 C++ 对象。 当thrown(引发)C++ 异常时,可能会导致资源泄漏和未定义的行为。

标准 C++ 异常处理

对安全展开堆栈对象的标准 C++ 异常处理模型的完全编译器支持需要 /EHsc(推荐)、/EHs/EHa

如果你使用 /EHs/EHsc,那么你的 catch(...) 子句不会catch(捕获)异步结构化异常。 任何访问冲突和托管 System.Exception 异常均不会被捕获。 而且,发生异步异常时范围内的对象不会被销毁,即使代码处理了异步异常也是如此。 此行为是一个参数,用于不处理结构化异常。 相反,请将这些异常视为是致命的。

当你使用 /EHs/EHsc 时,编译器假定异常只在 throw 语句或函数调用处发生。 此假设使编译器能够消除代码以跟踪很多不可展开对象的生存期,并且这也能显著减小代码大小。 如果使用 /EHa,可执行映像可能更大且较慢,因为编译器不会主动优化 try 块。 它还保留了自动清理本地对象的异常过滤器,即使编译器没有看到任何可以throw(引发)C++ 异常的代码。

结构化和标准 C++ 异常处理

/EHa 编译器选项启用异步异常和 C++ 异常的安全堆栈展开。 它支持使用本机 C++ catch(...) 子句处理标准 C++ 和结构化异常。 若要在不指定 /EHa 的情况下实现 SEH,则可以使用 __try__except__finally 语法。 有关详细信息,请参阅结构化异常处理

重要

指定 /EHa 并try(尝试)使用 catch(...) 处理所有异常可能很危险。 在大多数情况下,异步异常是不可恢复的,因而被认为是致命的。 捕获它们并继续可能导致进程损坏并使 Bug 难以发现和修复。

尽管 Windows 和 Visual C++ 支持 SEH,但我们强烈建议你使用 ISO 标准 C++ 异常处理(/EHsc/EHs)。 它提高了代码的可移植性和灵活性。 有时你可能仍必须在遗留代码或特定类型的程序中使用 SEH。 例如,在为支持公共语言运行时 (/clr) 而编译的代码中需要它。 有关详细信息,请参阅结构化异常处理

我们建议你切勿将使用 /EHa 编译的目标文件链接到在同一可执行模块中使用 /EHs/EHsc 编译的目标文件。 如果必须通过在模块中的任意位置使用 /EHa 来处理异步异常,请在此模块中使用 /EHa 来编译所有代码。 你可以在与使用 /EHs 编译的代码相同的模块中使用结构化异常处理语法。 但是,你不能在同一个函数中将 SEH 语法与 C++ trythrowcatch 混合使用。

如果要catch(捕获)由 throw 以外的内容引发的异常,请使用 /EHa。 以下示例将生成并catches(捕获)结构化异常:

// compiler_options_EHA.cpp
// compile with: /EHa
#include <iostream>
#include <excpt.h>
using namespace std;

void fail()
{
    // generates SE and attempts to catch it using catch(...)
    try
    {
        int i = 0, j = 1;
        j /= i;   // This will throw a SE (divide by zero).
        printf("%d", j);
    }
    catch(...)
    {
        // catch block will only be executed under /EHa
        cout << "Caught an exception in catch(...)." << endl;
    }
}

int main()
{
    __try
    {
        fail();
    }

    // __except will only catch an exception here
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        // if the exception was not caught by the catch(...) inside fail()
        cout << "An exception was caught in __except." << endl;
    }
}

/clr 下的异常处理

/clr 选项暗指 /EHa(即 /clr /EHa 是冗余的)。 如果在 /clr 后使用 /EHs/EHsc,编译器将产生一个错误。 优化不会影响此行为。 当捕获到某个异常时,编译器会为与异常处于同一范围内的任何对象调用类析构函数。 如果未捕获异常,则不会运行这些析构函数。

有关 /clr 下的异常处理限制的信息,请参见 _set_se_translator

运行时异常检查

/EHr 选项将在具有 noexcept 属性的所有函数中强制运行时终止检查。 默认情况下,如果编译器后端确定该函数仅调用non-throwing(非引发)函数,则运行时检查可能被优化掉。 Non-throwing(非引发)函数是具有一个指定不会thrown(引发)任何异常的属性的函数。 包括标记为 noexceptthrow()__declspec(nothrow) 的函数,当指定了 /EHc 时,还包括 extern "C" 函数。 Non-throwing(非引发)函数还包括编译器已通过检查确定为Non-throwing(非引发)的任何函数。 你可以使用 /EHr- 显式设置默认行为。

Non-throwing(非引发)属性不保证异常不能由函数thrown(引发)。 与 noexcept 函数的行为不同,MSVC 编译器将使用 throw()__declspec(nothrow)extern "C" 声明的函数thrown(引发)的异常视为未定义行为。 使用这三个声明属性的函数不强制使用运行时终止检查确定是否存在异常。 你可以使用 /EHr 选项来帮助你识别这种未定义的行为,方法是强制编译器为转义 noexcept 函数的未处理异常生成运行时检查。

在 Visual Studio 中或以编程方式设置选项

在 Visual Studio 开发环境中设置此编译器选项

  1. 打开项目的“属性页” 对话框。 有关详细信息,请参阅在 Visual Studio 中设置 C++ 编译器和生成属性

  2. 依次选择“配置属性”>“C/C++”>“代码生成”。

  3. 修改 “启用 C++ 异常” 属性。

    或者,将 “启用 C++ 异常” 设置为 “No”,然后在 “命令行” 属性页中的 “附加选项” 框中键入编译选项。

以编程方式设置此编译器选项

另请参阅

MSVC 编译器选项
MSVC 编译器命令行语法
错误和异常处理
异常规范 (throw)
Structured Exception Handling (C/C++)