Стандарт (см. п. 18.6 в [1]) позволяет подменить вызов стандартной функции terminate() функцией, определенной программистом. Как уже было сказано, эта функция вызывается либо из функции unexpected(), если нарушена спецификация исключения, либо механизмом обработки исключений, если отсутствует подходящая секция-ловушка. Аналогично можно подменить и вызов функции
unexpected (). В стандартной библиотеке (надо подключить заголовок exception >) прописаны следующие объявления:
typedef void (*unexpected_handler)();
unexpected_handler set_unexpected(unexpected_handler f) throw(); void unexpectedO ;
typedef void (*terminate_handler)();
terminate_handler set_terminate(terminate_handler f) throwQ; void terminateO ;
Эти объявления показывают, что для подмены стандартной функции terminateO мы должны определить собственную функцию с прототипом
void F():
Затем нужно прописать ее имя в вызове функции set_terminate(): set_terminate(F);
Можно сохранить адрес прежнего обработчика: void (*old__terminate) () = set_terminate(F);
После этого вместо termi nate () при обработке неперехваченных исключений будет вызываться наша функция F (). Простой пример демонстрирует это (листинг 7.18). Исполнять его необходимо из командной строки, так как при запуске в интегрированной среде она (среда) перехватывает все необработанные исключения.
Листинг 7.18. Подмена функции
terminateO void f ()
{ cout << "Неперехваченное исключение!" << endl: } int main()
{ set_terminate(f): // установка нашей функции
try { throw 1; } // генерация неперехватываемого исключения
catch(double) {}; return 0:
}
При трансляции в системе Visual C++.NET 2003 на экране появятся следующие сообщения:
Неперехваченное исключение!
This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information.
Первое сообщение, очевидно, наше. А вот следующие два — это то, что система вставляет в нашу программу при трансляции исходного текста.
Система С++ Builder 6 ведет себя немного иначе — на экране появляются сообщения:
Неперехваченное исключение! Abnormal program termination
Последнее сообщение — это сообщение стандартной функции завершения abort () (см. п. 18.3 в [1]).
Функция-«терминатор» не должна возвращать управление оператором return, не должна генерировать исключение оператором throw — она может лишь завершить программу функцией exit() или abort(). Но перед завершением можно выполнить действия, которые позволят разобраться в причине вызова «терминатора», например, сохранить информацию об ошибке в файле.
Аналогично реализуется подмена стандартной функции unexpectedO, только надо вызвать другую функцию set_...:
set_unexpected(f); // установка нашей функции
Функция обработки неперехваченного исключения, как и функция-терминатор, не должна возвращать управление оператором return и может завершить программу функцией exit() или abort(). Однако помимо этого она может сгенерировать исключение, заданное в спецификации исключений. Произойдет подмена неперехваченного исключения «легальным», и далее обработка исключений пойдет «нормальным» путем.
Функция может сгенерировать другое исключение, не указанное в спецификации, или просто «отправить» незаявленное исключение «дальше» оператором throw. В этом случае, если в спецификации исключений отсутствует исключение bad_exception, вызывается функция terminate(). А вот если спецификация исключений содержит исключение bad__exception, то сгенерированное исключение подменяется на bad_exception и начинается поиск его обработчика.
Как использовать подмену функции unexpectedO с пользой — показал Скотт Мейерс в [24]. Так как предвидеть тип неожиданного исключения невозможно, предлагается подменить все неперехваченные исключения одним типом: либо стандартным исключением bad_exception, либо нашим собственным типом, например UnexpectedException. Первый вариант реализуется функцией
void convertUnexpectedToBad_Exception()
{ throw; // генерация bad_exception
}
set_unexpected(convertUnexpectedToBad_Exception);
Второй вариант реализуется так:
// наше исключение
// генерация нашего исключения
class UnexpectedException {}; void convertUnexpectedO { throw UnexpectedExceptionO; }
set_unexpected(convertUnexpected);
Во все спецификации исключений функций надо включить соответствующий тип — и можно забыть о неожиданных исключениях. |