Различия в обработке исключений
Основное отличие между структурной обработкой исключений и обработкой исключений языка C++ заключается в том, что модель обработки исключений языка C++ основана на типах, а модель структурной обработки исключений языка C основана на исключениях одного типа — unsigned int. То есть, исключения языка C идентифицируются целым числом без знака, тогда как исключения языка C++ идентифицируются типом данных. При возникновении исключения в языке C все возможные обработчики используют фильтр, который проверяет контекст исключения языка C и определяет, следует ли принять исключение, передать его какому-либо другому обработчику или игнорировать исключение. При возникновении исключения в языке C++ оно может быть любого типа.
Второе отличие заключается в том, что модель структурной обработки исключений языка C называется "асинхронной", так как исключения возникают в дополнение в обычному управлению ходом выполнения. Механизм обработки исключений языка C++ полностью "синхронный", т. е. исключения возникают только в момент их создания.
В случае возникновения исключения языка C в программе на языке C++ оно может быть обработано обработчиком структурированного исключения с помощью связанного с ним фильтра или обработчиком C++ catch, в зависимости от того, который из них окажется динамически ближе к контексту исключения. Например, следующая программа на языке C++ создает исключение языка C внутри контекста C++ try:
Пример
// 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;
}
}
Например, следующий код устанавливает пользовательскую функцию преобразования, а затем создает исключение языка C, которое находится в оболочке класса SE_Exception:
// 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() );
}
}
Класс-оболочка исключения языка C
В простом примере, аналогичном показанному выше, исключение языка C может перехватываться только обработчиком catch с многоточием (...). Обработчику не передается никакая информация о типе или характере исключения. Хотя этот метод работает, в некоторых случаях может потребоваться определить преобразование между двумя моделями обработки исключений, чтобы каждое исключение языка C было связано с определенным классом. Для этого можно определить класс-оболочку исключения языка C, который будет использоваться (или на основе которого будут создаваться производные классы) для назначения определенного типа класса исключению языка C. В результате каждое исключение языка C может обрабатываться обработчиком catch языка C++ более выборочно, чем в предыдущем примере.
Класс-оболочка может иметь интерфейс, состоящий из некоторых функций-членов, которые определяют значение исключения; этот интерфейс может получать расширенные сведения о контексте исключения, предоставляемые моделью исключений языка 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. Внутри функции преобразования может создаваться любое типизированное исключение (вероятно, типа SE_Exception или типа класса, производного от SE_Exception), которое может перехватываться соответствующим обработчиком catch языка C++. Функция преобразования может просто производить возврат — это означает, что она не обработала исключение. Если функция преобразования сама вызывает исключение языка C, вызывается функция terminate.
Чтобы указать пользовательскую функцию преобразования, вызовите функцию _set_se_translator с именем функции преобразования в качестве единственного аргумента. Созданная функция преобразования вызывается по одному разу для каждого вызова функции для стека, имеющего блоки try. Функции преобразования по умолчанию не существует; если не задать эту функцию с помощью вызова _set_se_translator, исключение языка C может перехватываться только обработчиком catch с многоточием.