Программирование >>  Разработка устойчивых систем 

1 2 3 [ 4 ] 5 6 7 ... 196


Только однозначно выбираемые и доступные базовые классы могут перехватывать исключения производных классов. Это правило сводит к минимуму затраты вре.мени выполнения, необходимые для проверки исключений. Помните, что исключения проверяются на стадии выполнения, а не на стадии компиляции, поэтому обширная информация, доступная при компиляции, во время обработки исключений недоступна.

try {

x.fO: } catch(X-Troubles) {

cout caught Trouble endl; Скрывается предыдущим обработчиком: } catch(X::Small&) {

cout caught Small Trouble endl: } catch(X::Big&) {

cout caught Big Trouble endl:

} III:-

В данном примере механизм обработки исключений всегда будет сопоставлять объект Trouble (и все, что является частным случаем Trouble по правилам открытого наследования) с первым обработчиком. Таким образом, второй и третий обработчики вообще никогда не вызываются, поскольку все исключения достаются первому обработчику. Логичнее начать с обработчиков производных типов и переместить обработчик базового типа в конец списка, чтобы перехватить все более общие случаи.

Обратите внимание: в этих примерах исю1ючения перехватываются по ссылке, хотя для данной иерархии это несущественно - производные классы не содержат дополнительных членов, а идентификаторы аргументов в обработчиках все равно не используются. Обычно в обработчиках следует работать с аргументами по ссылке, а не по значению, чтобы избежать усечения информации.

Перехват любых исключений

Иногда требуется написать обработчик для перехвата любых типов исключений.

Для этой цели используется специальный список аргументов в виде многоточия

(...):

catch(...) { cout an exception was thrown endl:

Поскольку такой обработчик перехватывает все исключения, он размещается в конце списка обработчиков (иначе следующие за ним обработчики никогда не выполняются).

Универсальный обработчик не может иметь аргументов, поэтому в нем невозможно получить какую-либо информацию об исключении или его типе. Такие секции catch часто освобождают некие ресурсы и перезапускают исключение.

Перезапуск исключения

Перезапуск исключений обычно применяют для освобождения тех или иных ресурсов (скажем, закрытия сетевых подключений или освобождения памяти в куче - за подробностями обращайтесь к разделу Управление ресурсами



этой главы). При возникновении исключения иногда бывает не важно, какая ошибка породила его - просто нужно закрыть подключение, открытое ранее, после чего передать обработку исключения в другой контекст, ближе к пользователю (то есть находящийся выше в цепочке вызовов). Конструкция catch(...) идеально подходит для таких случаев. Вы хотите перехватить любые исключения, освободить ресурс, а затем перезапустить исключение для последующей обработки. Исключения перезапускаются командой throw без аргумента внутри обработчика:

catch(...) { cout an exception was thrown endl: Освобождение ресурсов throw:

Остальные секции catch того же блока try игнорируются - команда throw передает исключение обработчикам следующего контекста. Вся информация объекта исключения сохраняется, поэтому обработчики внешнего контекста, перехватывающие конкретные типы исключений, смогут извлечь любую информацию, содержащуюся в объекте.

Неперехваченные исключения

Как объяснялось в начале главы, обработка исключений лучше традиционной методики с возвратом кода ошибки, поскольку исключения не могут игнорироваться, а обработка исключения отделяется от непосредственно решаемой задачи. Если ни один из обработчиков, следующих за блоком try, не соответствует типу исключения, то исключение передается в контекст следующего уровня, то есть в функцию (или в блок try), в которой находится блок try, не перехвативший ис1слючение, причем местонахождение этого внешнего блока try не всегда очевидно, поскольку он находится на более высоком уровне иерархии. Процесс продолжается до тех пор, пока в какой-то момент для исключения не будет найден подходящий обработчик. В этот момент ис1слючение считается перехваченным , и дальнейший поиск обработчиков для него не выполняется.

Функция terminateO

Если исключение не будет перехвачено ни одним обработчиком какого-либо уровня, автоматически вызывается стандартная библиотечная функция terminate(). По умолчанию terminate() вызывает функцию abort() из стандартной библиотеки С, что приводит к аварийному завершению программы. В системах семейства Unix функция abort() также выводит дамп памяти. Вызов abort() отменяет стандартную процедуру завершения программы, а это означает, что деструкторы глобальных и статических объектов не вызываются. Функция terminate() также выполняется в том случае, если деструктор локального объекта генерирует исключение в процессе раскрутки стека (во время обработки текущего исключения) или исключение происходит в конструкторе или деструкторе глобального или статического объекта (в общем случае не стоит разрешать запуск исключений в деструкторах).



void (*old terminate)() = set terminate(terminator):

class Botch { public: class Fruit {}: void f() { cout Botch::f() endl: throw FruitO:

-BotchO { throw c: }

int mainO { try { Botch b: b.fO: } catchC.) { cout inside catch(...) endl:

} III:-

Определение old terminate на первый взгляд выглядит несколько странно: мы не только создаем указатель на функцию, но и инициализируем его возвращаемым значением set terminate(). Хотя после указателя на функцию обычно следует точка с запятой, в действительности это самая обычная переменная, которая может инициализироваться при определении.

Класс Botch запускает исключение не только в f(), но и в деструкторе. Как нетрудно убедиться, это приводит к вызову terminate(). Хотя в обработчике

Функция set terminate()

Вы можете заменить стандартную функцию terminate() собственной версией при помощи стандартной функции set terminate(), которая возвращает указатель на заменяемую функцию terminate() (при первом вызове это будет стандартная библиотечная версия) для ее последующего восстановления. Пользовательская функция terminateO должна вызываться без аргументов и возвращать void. Кроме того, устанавливаемые обработчики не могут возвращать управление или запускать ис-101ючения, они лишь реализуют некую логику завершения программы. Вызов функции terminateO означает, что проблема неразрешима.

Применение функции set terminate() продемонстрировано в следующем примере. Сохранение и восстановление возвращаемого значения поможет локализовать фрагмент кода, в котором произошло неперехваченное исключение:

: С01:Terminator.срр

Использование set terminate().

Программа также демонстрирует обработку неперехваченных исключений. #include <exception> linclude <iostream> using namespace std:

void terminatorO { cout Ill be back! endl; exit(O):



1 2 3 [ 4 ] 5 6 7 ... 196

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика