Обработчики производных классов следует размещать до обработчиков базовых, так как в ином случае они никогда не получат управление.




Общий механизм обработки исключений

То место, в котором может возникнуть ошибка, обязательно должно быть расположено в контролируемом блоке. Под контролируемым блоком, в данном случае, понимается составной оператор, перед которым стоит ключевое слово try.

Обработка исключений реализуется следующим образом:

    1. Выявление ошибки в исполняющейся функции.
    2. Генерация исключения функцией, в которой была обнаружена ошибка.
    3. Поиск обработчика исключения.
    4. Если обработчик был найден, то происходит передача управления найденному обработчику.
    5. Если обработчик не был найден, выполняется вызов стандартной функции terminate, которая передаёт управление функции abort, назначение которой состоит в аварийном завершении работы процесса.

Для генерации исключения используется ключевое слово throw с параметром, который определяет вид исключения. Сам параметр может быть переменной, константой или же объектом и используется для передачи информации об исключении его обработчику.

Вместо функции abort можно установить свою функцию завершения процесса.

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

Синтаксис исключения

Для обозначения контролируемого блока используется ключевое слово try. Под контролируемым блоком понимается программный код, в котором может быть сгенерировано исключение. Сам же блок заключается в фигурные скобки.

try{ <…> }

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

Порождение же исключения происходит с помощью ключевого слова throw:

throw [список_параметров]

Тип данных выражения, стоящего после throw, определяет тип порождаемого исключения. Соответственно, во время порождения исключения, выполнение текущего блока приостанавливается. Как правило, генерация исключения осуществляется непосредственно в функциях, которые прямо или косвенно вложены в try-блок, а не в нём самом.

Исключение не всегда может быть правильно обработано в момент его появления. В таком случае, используются вложенные контролируемые блоки, а исключение передаётся на более высокий уровень с помощью ключевого слова throw.

Сами обработчики исключений начинаются с ключевого слова catch, после которого в скобках следует тип обрабатываемого исключения. Соответственно обработчик должен располагаться непосредственно за контролируемымблоком.

Синтаксис обработчика выглядит следующим образом:

catch(тип идентификатор){ /*Тело обработчика*/ }
catch(тип){ /*Тело обработчика*/ }
catch(…){ /*Тело обработчика*/ }

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

Вторая форма предполагает то, что информация об исключении ни коим образом не будет использоваться и играть роль будет только тип исключения.

В третьей форме вместо параметра используется многоточие. Оно показывает то, что обработчик будет перехватывать все исключения.

Важно всегда иметь ввиду то, что обработчики просматриваются в порядке их следования. Именно поэтому обработчик третьего типа стоит размещать в самом конце, когда не подходит ни один из предшествующих.

Пример 1. Описание обработчиков исключений. catch (int val){ /**/ cerr << “We caught an int exception with value.” << val << endl; } catch (double){ /*Обработка исключения типа double*/ cerr << "We caught an exception of type double" << endl; }

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

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

Когда происходит порождение исключения с помощью throw, функции исполнительной библиотеки C++ выполняют следующий набор действий:

    1. создание копии параметра throw в виде статического объекта, который существует до тех пор, пока исключение не будет обработано;
    2. раскручивание стека, вызывая деструкторы локальных объектов, которые выходят из соответствующей области действия;
    3. передача управления обработчику, который имеет параметр, совместимый по типу с передаваемым объектом.

В процессе раскручивания стека все обработчики на каждом уровне просматриваются последовательно, от самого внутреннего блока к внешнему, до тех пор, пока не будет найден подходящий обработчик.

Обработчик считается найденным в следующих случаях:

          • если тип объекта, указанный после throw, совпадает с параметром, указанным в обработчике (catch) (параметр может быть записан в форме Type, const Type, Type& или const Type&, где Type – это тип исключения);
          • если тип объекта, указанный после throw, является дочерним от указанного в качестве параметра обработчика прерываний (catch), с учётом того, что наследование производилось с ключом доступа public;
          • если тип объекта, указанный после throw, является указателем, который может преобразован в соответствии со стандартными правилами преобразования указателей к типу указателя, указанного в качестве параметра обработчика исключения (catch).

Обработчики производных классов следует размещать до обработчиков базовых, так как в ином случае они никогда не получат управление.

Обработчик указателя типа void автоматически скрывает указатель любого другого типа данных. Именно по этой причине его тоже следует размещать после обработчиков указателей определённых типов.

Существует ряд распространённых ошибок, которые необходимо отслеживать, например, при использовании функции sqrt. К таким ошибкам относится, например, то, когда появляется отрицательное число, далее передаваемое функции в качестве параметра.

Пример 2. Обработка исключений. #include <iostream> #include "math.h" using namespace std; int main(){ cout << "Enter a number: "; double num; cin >> num; /*Отслеживание исключений*/ try{ /* Исключение, если пользователь ввёл отрицательное число. */ if (num < 0.0) throw "Can not take sqrt of negative number"; /* Выполнение операции и вывод результата, если введено число >=0. */ cout << "The sqrt of " << num << " is " << sqrt(a) << '\n'; } catch (const char* exception){ cerr << "Error: " << exception << '\n'; } }

Если исключение было направлено в обработчик, то на этом этапе оно считается обработанным, даже если соответствующий блок пуст, т.е. ничего не делает. Тем не менее, если у разработчика есть желание сделать что-то полезное в обработчике, существует три распространённые вещи, которые выполняют обработчики, когда они ловят исключение:

    1. обработчик может вывести сообщение об ошибке;
    2. обработчик может выполнить возврат значения или кода ошибки;
    3. обработчик может породить другое исключение, и оно будет обрабатываться следующим контролируемым блоком, так как обработчик не находится внутри try-блока.


Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2020-06-05 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: