处理 C++ 中的结构性异常

C 结构化异常处理 (SEH) 和 C++ 异常处理的主要区别是,C++ 异常处理模型处理的是多个类型,而 C 结构化异常处理模型处理的是一个类型(具体来说就是 unsigned int)的异常。 即,C 异常由无符号整数值标识,而 C++ 异常由数据类型标识。 当 C 中引发了结构化异常时,每个可能的处理程序都将执行筛选器来检查 C 异常上下文,并确定是接受该异常、将其传递给其他处理程序还是忽略它。 当 C++ 中引发了异常时,该异常可以是任何类型。

第二个区别是,C 结构化异常处理模型被称为是“异步的”,因为异常是从属在正常控制流之后发生的。 C++ 异常处理机制是完全“同步的”,这意味着异常仅在被引发时发生。

使用 /EHs 或 /EHsc 编译器选项时,C++ 异常处理程序均不会处理结构化异常。 这些异常仅由 __except 结构化异常处理程序或 __finally 结构化终止处理程序进行处理。 有关详细信息,请参阅结构化异常处理 (C/C++)

/EHa 编译器选项下,如果在 C++ 程序中引发了 C 异常,可以由结构化异常处理程序与其关联筛选器一起处理,或者由 C++ catch 处理程序进行处理,具体取决于哪一种更加动态接近异常上下文。 例如,此示例 C++ 程序在 C++ try 上下文中引发了一个 C 异常:

示例 - 在 C++ catch 块中捕获 C 异常

// exceptions_Exception_Handling_Differences.cpp
// compile with: /EHa
#include <iostream>

using namespace std;
void SEHFunc( void );

int main() {
   try {
      SEHFunc();
   }
   catch( ... ) {
      cout << "Caught a C exception."<< endl;
   }
}

void SEHFunc() {
   __try {
      int x, y = 0;
      x = 5 / y;
   }
   __finally {
      cout << "In finally." << endl;
   }
}
In finally.
Caught a C exception.

C 异常包装类

在与上述类似的简单示例中,C 异常只能由省略号 (...) catch 处理程序捕获。 有关类型或异常性质的信息不传递给该处理程序。 尽管此方法有效,但在某些情况下,你可能需要定义两个异常处理模型之间的转换,使每个 C 异常与一个特定类关联。 要进行转换,你可定义 C 异常“包装”类,可使用该类或从中进行派生来将特定类类型特性化为 C 异常。 这样,每个 C 异常都可以由特定的 C++ catch 处理程序单独处理,而不是在单个处理程序中处理所有异常。

您的包装器类可能有一个接口,该接口包含一些成员函数,用来确定异常的值以及访问 C 异常模型提供的扩展异常上下文信息。 你可能还希望定义一个默认构造函数、一个接受 unsigned int 参数(用于提供基础 C 异常表示形式)的构造函数和一个按位复制构造函数。 下面是 C 异常包装类的一个可能的实现:

// exceptions_Exception_Handling_Differences2.cpp
// compile with: /c
class SE_Exception {
private:
   SE_Exception() {}
   SE_Exception( SE_Exception& ) {}
   unsigned int nSE;
public:
   SE_Exception( unsigned int n ) : nSE( n ) {}
   ~SE_Exception() {}
   unsigned int getSeNumber() {
      return nSE;
   }
};

要使用此类,请安装每次引发 C 异常时由内部异常处理机制调用的自定义 C 异常转换函数。 在转换函数中,可引发可由适当匹配的 C++ catch 处理程序捕获的任意类型的异常(可能是 SE_Exception 类型,或派生自 SE_Exception 的类类型)。 转换函数可能直接返回,这表示它没有处理异常。 如果转换函数本身引发了 C 异常,则会调用 terminate

若要指定自定义转换函数,请使用转换函数的名称作为 _set_se_translator 函数的单一参数来调用它。 对于具有 try 块的堆栈上的每个函数调用,将调用一次编写的转换器函数。 没有默认转换函数;如果你未通过调用 _set_se_translator 来指定转换函数,C 异常只能由省略号 catch 处理程序捕获。

示例 - 使用自定义转换函数

例如,以下代码安装了自定义转换函数,然后引发了由 SE_Exception 类包装的 C 异常:

// exceptions_Exception_Handling_Differences3.cpp
// compile with: /EHa
#include <stdio.h>
#include <eh.h>
#include <windows.h>

class SE_Exception {
private:
   SE_Exception() {}
   unsigned int nSE;
public:
   SE_Exception( SE_Exception& e) : nSE(e.nSE) {}
   SE_Exception(unsigned int n) : nSE(n) {}
   ~SE_Exception() {}
   unsigned int getSeNumber() { return nSE; }
};

void SEFunc() {
    __try {
        int x, y = 0;
        x = 5 / y;
    }
    __finally {
        printf_s( "In finally\n" );
    }
}

void trans_func( unsigned int u, _EXCEPTION_POINTERS* pExp ) {
    printf_s( "In trans_func.\n" );
    throw SE_Exception( u );
}

int main() {
    _set_se_translator( trans_func );
    try {
        SEFunc();
    }
    catch( SE_Exception e ) {
        printf_s( "Caught a __try exception with SE_Exception.\n" );
        printf_s( "nSE = 0x%x\n", e.getSeNumber() );
    }
}
In trans_func.
In finally
Caught a __try exception with SE_Exception.
nSE = 0xc0000094

另请参阅

混合使用 C(结构化)和 C++ 异常