|
Программирование >> Инициализация объектов класса, структура
void calculate( int op ) { try { mathFunc( op ); catch ( EHstate eCbj ) { / / eCbj - копия сгенерированного объекта-искчения созданного выражением throw. Объявление исключения в этом примере напоминает передачу параметра по значению. Объект eCbj инициализируется значением объекта-исключения точно так же, как переданный по значению формальный параметр функции - значением соответствующего фактического аргумента. (Передача параметров по значению рассматривалась в разделе 7.3.) Как и в случае параметров функции, в объявлении исключения может фигурировать ссылка. Тогда catch-обработчик будет напрямую ссылаться на объект-исключение, void calculate( int op ) { try { mathFunc( op ); catch ( EHstate &eCbj ) { / / eCbj сс1лается на сгенерированн объект-искчение сгенерированный выражением throw, а не создавать его локальную копию: Для предотвращения ненужного копирования больших объектов применять ссылки следует не только в объявлениях параметров типа класса, но и в объявлениях исключений того же типа. В последнем случае catch-обработчик сможет модифицировать объект-исключение. Однако переменные, определенные в выражении throw, остаются без изменения. Например, модификация eCbj внутри catch-обработчика не затрагивает глобальную void calculate( int op ) { try { mathFunc( op ); catch ( EHstate &eCbj ошибку eCbj = noErr; глобальная переменная state не изменилась / / исправить ошибку, вызвавшую искчение ; глобальная переменная st переменную state, установленную в выражении throw: calculate() вызывает определенную выше miathFunc (). При входе в catch-обработчик внутри calculate() объект eCbj инициализируется копией объекта-исключения, Catch-обработчик переустанавливает eCbj в noErr после исправления ошибки, вызвавшей исключение. Поскольку eCbj - это ссылка, можно ожидать, что присваивание модифицирует глобальную переменную state. Однако изменяется лишь объект-исключение, созданный в выражении throw, поэтому модификация eCbj не затрагивает state. 11.3.2. Раскрутка стека Поиск catch-обработчикадля возбужденного исключения происходит следующим образом. Когда выражение throw находится в try-блоке, все ассоциированные с ним предложения catch исследуются с точки зрения того, могут ли они обработать исключение. Если подходящее предложение catch найдено, то исключение обрабатывается. В противном случае поиск продолжается в вызывающей функции. Предположим, что вызов функции, выполнение которой прекратилось в результате исключения, погружен в try-блок; в такой ситуации исследуются все предложения catch, ассоциированные с этим блоком. Если один из них может обработать исключение, то процесс заканчивается. В противном случае переходим к следующей по порядку вызывающей функции. Этот поиск последовательно проводится во всей цепочке вложенных вызовов. Как только будет найдено подходящее предложение, управление передается в соответствующий обработчик. В нашем примере первая функция, для которой нужен catch-обработчик, - это функция-член pop() класса iStack. Поскольку выражение throw внутри pop() не находится в try-блоке, то программа покидает pop() , не обработав исключение. Следующей рассматривается функция, вызвавшая pop() , то есть main() . Вызов pop() внутри miain() находится в try-блоке, и далее исследуется, может ли хотя бы одно ассоциированное с ним предложение catch обработать исключение. Поскольку обработчик исключения popCnEmpty имеется, то управление попадает в него. Процесс, в результате которого программа последовательно покидает составные инструкции и определения функций в поисках предложения catch, способного обработать возникшее исключение, называется раскруткой стека. По мере раскрутки прекращают существование локальные объекты, объявленные в составных инструкциях и определениях функций, из которых произошел выход. C++ гарантирует, что во время описанного процесса вызываются деструкторы локальных объектов классов, хотя они исчезают из-за возбужденного исключения. (Подробнее мы поговорим об этом в главе 19.) Если в программе нет предложения catch, способного обработать исключение, оно остается необработанным. Но исключение - это настолько серьезная ошибка, что программа не может продолжать выполнение. Поэтому, если обработчик не найден, вызывается функция terminate() из стандартной библиотеки C++. По умолчанию terminate() активизирует функцию abort() , которая аномально завершает программу. (В большинстве ситуаций вызов abort() оказывается вполне приемлемым решением. Однако иногда необходимо переопределить действия, выполняемые функцией terminate(). Как это сделать, рассказывается в книге [STROUSTRUP97].) Вы уже, наверное, заметили, что обработка исключений и вызов функции во многом похожи. Выражение throw ведет себя аналогично вызову, а предложение catch чем-то напоминает определение функции. Основная разница между этими двумя механизмами заключается в том, что информация, необходимая для вызова функции, доступна во время компиляции, а для обработки исключений - нет. Обработка исключений в C++ требует языковой поддержки во время выполнения. Например, для обычного вызова catch ( exception eCbj ) { if ( canHandle( eCbj ) ) обработать исключение return; else повторно возбудить исключение, чтобы его перехватил другой catch-обработчик throw; внутри составной инструкции, являющейся частью catch-обработчика: При повторном возбуждении новый объект-исключение не создается. Это имеет значение, если catch-обработчик модифицирует объект, прежде чем возбудить исключение enum EHstate { noErr, zeroCp, negativeCp, severeError }; void calculate( int op ) { try { исключение, возбужденное mathFunc(), имеет значение zeroCp mathFunc( op ); catch ( EHstate eCbj ) { что-то исправить / / пахтаемся модифицировать объект-искчение eCbj = severeErr; предполагалось, что повторно возбужденное исключение будет иметь значение severeErr throw; повторно. В следующем фрагменте исходный объект-исключение не изменяется. Почему? функции компилятору в точке активизации уже известно, какая из перегруженных функций будет вызвана. При обработке же исключения компилятор не знает, в какой функции находится catch-обработчик и откуда возобновится выполнение программ:. Функция terminate() предоставляет механизм времени выполнения, который извещает пользователя о том, что подходящего обработчика не нашлось. 11.3.3. Повторное возбуждение исключения Может оказаться так, что в одном предложении catch не удалось полностью обработать исключение. Выполнив некоторые корректирующие действия, catch-обработчик может решить, что дальнейшую обработку следует поручить функции, расположенной выше в цепочке вызовов. Передать исключение другому catch-обработчику можно с помощью повторного возбуждения исключения. Для этой цели в языке предусмотрена конструкция throw; которая вновь генерирует объект-исключение. Повторное возбуждение возможно только
|
© 2006 - 2025 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки. |