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

Материал из Deeptown Manual
Перейти к: навигация, поиск
Строка 50: Строка 50:
 
На практике, применяются специальные классы исключений, которые объявлены еще в [[Стандартная библиотека Gide|стандартной библиотеке]]. Таким образом, все языки использующие стандартную библиотеку и умеющие работать с исключениями, смогут успешно взаимодействовать с помощью этого механизма.
 
На практике, применяются специальные классы исключений, которые объявлены еще в [[Стандартная библиотека Gide|стандартной библиотеке]]. Таким образом, все языки использующие стандартную библиотеку и умеющие работать с исключениями, смогут успешно взаимодействовать с помощью этого механизма.
  
Приведем некоторые из наиболее распространенных классов исключений и поясним из назначение:
+
Приведем некоторые из наиболее распространенных классов исключений и поясним их назначение:
  
 
::{| width=60%
 
::{| width=60%
Строка 79: Строка 79:
 
<!--* <tt></tt> — -->
 
<!--* <tt></tt> — -->
  
Здесь приведены только наиболее общие описания классов. Более конкретная информация, содержится в самом объекте исключения. Классы исключений необходимы для того чтобы сортировать возникающие ошибки по типам, и на основе этой информации обрабатывать их различным образом.
+
Здесь приведены только наиболее общие описания классов. Более конкретная информация содержится в самом объекте исключения. Классы исключений необходимы для того чтобы сортировать возникающие ошибки по типам, и на основе этой информации обрабатывать их различным образом.
  
 
Например, в описанном выше случае, наиболее подходящим классом ошибки будет <tt>e_range_error</tt>. Соответственно, код генерации ошибки можно написать так:
 
Например, в описанном выше случае, наиболее подходящим классом ошибки будет <tt>e_range_error</tt>. Соответственно, код генерации ошибки можно написать так:
Строка 98: Строка 98:
 
}
 
}
 
</source>
 
</source>
 +
 +
Для того, чтобы сослаться на объект исключения используется переменная ''e''. Все системные классы исключений имеют свойства <tt>name</tt> и <tt>description</tt>. Первое содержит имя исключения, а второе — описание ошибки.
 +
 +
В зависимости от класса исключения могут добавляться дополнительные поля, более точно характеризующие ошибку. Для того чтобы можно было классифицировать объект исключения по его классу, применяется расширенная форма конструкции перехвата:
 +
 +
<source lang=kpp>
 +
try {
 +
    /* защищаемый код */
 +
} catch (/*класс 1*/ /*имя объекта 1*/) {
 +
    /* код обработки исключения 1 */
 +
} catch (/*класс 2*/ /*имя объекта 2*/) {
 +
    /* код обработки исключения 2 */
 +
...
 +
}
 +
</source>
 +
 +
Таким образом, при объявлении переменной исключения, мы можем указывать ее тип и потом ссылаться на
 +
специфические поля объектов, соответствущих этому типу:
 +
<source lang=kpp>
 +
try {
 +
    /* защищаемый код */
 +
} catch (e_again e) {
 +
    /* запрос у пользователя на повторение операции */
 +
} catch (e_range_error e) {
 +
    /* отображение ошибки */
 +
} catch (e) {
 +
    /* запись в лог файл */
 +
}
 +
</source>
 +
 +
В вышеприведенном коде используются три обработчика исключений: первые два перехватывают исключения определенного типа, а третий — все оставшиеся. Структура обработчиков исключений чем то напоминает конструкцию <tt>'''[[Основные синтаксические конструкции#Оператор множественного выбора (switch)|switch]]'''</tt>.

Версия 16:12, 23 сентября 2007

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

Содержание

Идеология исключений

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

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

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

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

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

В некоторых местах, где заранее предусматривается возможность проявления ошибки (например при открытии файла) вставляется специальная конструкция исключения. Она состоит из основного блока и одного или нескольких блоков — перехватчиков исключений. Выглядит эта конструкция так: <source lang="kpp"> try {

   /* try-блок (основное тело) */

} catch (/* переменная для объекта исключения 1 */) {

   /* код перехватчика 1 */

} catch (/* переменная для объекта исключения 2 */) {

   /* код перехватчика 2 */

} </source>

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

Объект исключения

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

Генерация исключения

Код, который первым обнаруживает исключительную ситуацию (то есть, стоит у ее истоков) и желающий сообщить о ней "наверх" должен создать объект исключения и "выбросить" его. Создается объект так же, как и любой другой объект в языке К++: либо с помощью оператора new, либо с помощью конструктора. "Выбрасывание" объекта осуществляется с помощью специального оператора throw, который принимает объект в качестве параметра. Обычно эти операции совмещают в одном действии.

Приведем пример некоторой функции, которая проверяет правильность передаваемых ей данных и выбрасывает исключение в случае несоответствия: <source lang="kpp"> function Process(const int idx) {

   if (! idx in 5 .. 10)
       throw "индекс должен быть в диапазоне от 5 до 10";
   /* нормальный код обработки */

} </source>

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

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

Приведем некоторые из наиболее распространенных классов исключений и поясним их назначение:

e_invalid_call неверный вызов (входные данные неверны)
e_failed операция не удалась
e_again требуется повтор операции
e_denied действие запрещено
e_cancelled операция была отменена
e_already операция уже была выполнена
e_range_error ошибка диапазона (выход за границы)
e_division_by_zero ошибка деления на ноль
e_timeout истекло время ожидания
e_stream_error ошибка потока
e_regexp_error ошибка при разборе регулярного выражения
e_abstract_error попытка вызова абстрактного метода

Здесь приведены только наиболее общие описания классов. Более конкретная информация содержится в самом объекте исключения. Классы исключений необходимы для того чтобы сортировать возникающие ошибки по типам, и на основе этой информации обрабатывать их различным образом.

Например, в описанном выше случае, наиболее подходящим классом ошибки будет e_range_error. Соответственно, код генерации ошибки можно написать так: <source lang="kpp"> throw e_range_error.create("индекс должен быть в диапазоне от 5 до 10"); </source>

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

Для обработки возникающих исключений используется конструкция перехвата. Она начинается с ключевого слова catch, следом за которым, в круглых скобках, идет описание переменной исключения, а затем, собственно, блок обработчика. Например, так мог бы выглядеть код вызова вышеописанной функции Process() и обработки возникающего исключения, если бы оно имело место: <source lang=kpp> try {

   Process(/*выражение задающее индекс*/);
   /* другой код */

} catch (e) {

   print("Ошибка обработки индекса. #{e.name}: #{e.description}\n");
   /* код обработки исключения */

} </source>

Для того, чтобы сослаться на объект исключения используется переменная e. Все системные классы исключений имеют свойства name и description. Первое содержит имя исключения, а второе — описание ошибки.

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

<source lang=kpp> try {

   /* защищаемый код */

} catch (/*класс 1*/ /*имя объекта 1*/) {

   /* код обработки исключения 1 */

} catch (/*класс 2*/ /*имя объекта 2*/) {

   /* код обработки исключения 2 */

... } </source>

Таким образом, при объявлении переменной исключения, мы можем указывать ее тип и потом ссылаться на специфические поля объектов, соответствущих этому типу: <source lang=kpp> try {

   /* защищаемый код */

} catch (e_again e) {

   /* запрос у пользователя на повторение операции */

} catch (e_range_error e) {

   /* отображение ошибки */

} catch (e) {

   /* запись в лог файл */

} </source>

В вышеприведенном коде используются три обработчика исключений: первые два перехватывают исключения определенного типа, а третий — все оставшиеся. Структура обработчиков исключений чем то напоминает конструкцию switch.

Персональные инструменты
Пространства имён

Варианты
Действия
Навигация
информация
документация
Инструменты