Процессорные исключения
Наибольший интерес для нас представляют исключения, связанные с процессором. Их, как уже говорилось, нельзя обрабатывать стандартными средствами C++. Для этих исключений в заголовке winbase.h определен ряд символических констант. Вот некоторые из них:
EXCEPTION_ACCESS_VIOLATION
EXCEPTION_ARRAY_BOUNDS_EXCEEDED
EXCEPTION_FLT_DENORMAL_OPERAND
EXCEPTION_FLT_DIVIDE_BY_ZERO
EXCEPTION_FLT_INEXACT_RESULT
EXCEPTION_FLT_INVALID_OPERATION
EXCEPTION_FLT_OVERFLOW
EXCEPTION_FLT_STACK_CHECK
EXCEPTION_FLT_UNDERFLOW
EXCEPTION_INT_DIVIDE_BY_ZERO
EXCEPTION_INT_OVERFLOW
EXCEPTION_PRIV_INSTRUCTION
EXCEPT ION_IN_PAGE_ERROR
EXCEPTION_ILLEGAL_INSTRUCTION
EXCEPTION_NONCONTINUABLE_EXCEPTION
EXCEPTION_STACK_OVERFLOW
EXCEPTION_INVALID_DISPOSITION
EXCEPTION_GUARD_PAGE
Ниже показан пример, в котором имитируется нарушение доступа путем разыменования нулевого указателя. Функция фильтра детектирует эту ошибку, и обработчик исключения выводит соответствующее собщение:
///////////////////////////////////////////////////
// Access.cpp: Применение SEH для перехвата
// системных исключений.
//
#include <except.h>
#include <iostream.h>
#pragma hdrstop
#include <condefs.h>
static int xfilter(EXCEPTION_POINTERS *info)
{
if (info->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
return EXCEPTION_EXECUTE_HANDLER;
else
return EXCEPTION_CONTINUE_SEARCH;
}
int main () {
try (
int *p = NULL;
*P = -1;
}
_except(xfilter(GetExceptionInformation())) { cerr << "Exception Access Violaton caught..." << endl;
exit(l);
} cout<<"Normal exit..." << endl;
return 0;
}
Для справки приведем описание структуры exception_pointers:
struct EXCEPTION_POINTERS {
EXCEPTION_RECORD *ExceptionRecord;
CONTEXT *Context;
};
Struct EXCEPTION_RECORD { DWORD ExceptionCode;
DWORD ExceptionFlags;
struct EXCEPTION_RECORD *ExceptionRecord;
void *ExceptionAddress;
DWORD NumberParameters;
DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
Как уже говорилось, структуры управления исключениями SEH и C++ могут быть вложены друг в друга. Более того, except-обработчик может выбрасывать исключение C++, которое будет далее перехватываться catch-обработчиком. Можно в целях удобства и единообразия заключить все критические участки кода, могущие возбудить процессорные исключения (или вообще всю программу) в блоки try/ except, выбрасывающие исключения C++, и затем обрабатывать их наравне с другими исключениями. Рассмотрите такой пример:
////////////////////////////////////////////////
// SehPlus.cpp: Переход от SEH к C++.
//
#include <iostream.h>
#include <excpt.h>
#include <stdexcept>
#pragma hdrstop
#include <condefs.h>
static EXCEPTIONJRECORD eRec;
static int xfliter(EXCEPTION_POINTERS *xp) {
eRec = *(xp->ExceptionRecord);
return EXCEPTION_EXECUTE_HANDLER;
}
int main () {
double d = 10000;
try { try {
for (int i=5; i>=0; i-) { d = d / i;
cout << i << "... ";
} }
_except- ixfliter(GetExceptionInformation ())) ( if (eRec.ExceptionCode ==
EXCEPTIOM_FLT_DIVIDE_BY_ZERO) throw runtime error(
"Floating point divide by zero!");
else
throw runtime_error(
"Unknown processor exception.");
} }
catch(const exception &e) { cout << e.what() << end1;
} return 0;
}
Программа выводит:
5... 4... 3...2... I... Floating point divide by zero!
Целесообразно было бы предусмотреть для процессорных исключений специальный класс, производный от, например, runtime_error, дополнив его структурой EXCEPTION RECORD. Тогда вообще вся обработка осуществлялась бы средствами C++.
Заключение
В этой главе вы познакомились со средствами C++, позволяющими сделать обработку ошибок и других исключительных ситуаций гораздо более единообразной и надежной, чем это было возможно когда-либо прежде. Кроме того, исключения используются стандартными библиотеками и в самом языке (класс string, операция new). Поэтому я всячески призываю вас практиковаться и привыкать к управлению исключениями.