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

Материал из Deeptown Manual
Перейти к: навигация, поиск
(Новая: Как уже было отмечено во введении, исключения — это мощный меха...)

Версия 15:09, 23 сентября 2007

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

_TOC_

Содержание

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

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

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

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

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

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

В некоторых местах, где заранее предусматривается возможность проявления ошибки (например при открытии файла) вставляется специальная конструкция исключения. Она состоит из основного блока и одного или нескольких блоков — перехватчиков исключений. Выглядит эта конструкция так: <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>

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

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