Глобальная обработка
Механизм глобальной обработки исключений реализуется через объект Application, который есть в любом приложении. При получении от операционной системы сообщения об исключении объект Application генерирует событие OnException, обработчик которого и является глобальным обработчиком исключений. По умолчанию на это событие для всех видов динамических ошибок, не имеющих своего обработчика, реагирует метод HandleException приложения. В теле этого метода вызывается метод ShowException приложения, выводящий на экран диалоговое окно с описанием возникшего исключения. Такая обработка не устраняет причину исключения, но обеспечивает пользователя информацией об ошибке и облегчает ее поиск и устранение.
Локальная обработка
Для работы с локальными обработчиками исключений в состав языка введены две конструкции: try … finally и try … except. Обе конструкции имеют похожий синтаксис, но разное назначение. Блоки try включают в себя инструкции программы, при выполнении которых может возникнуть исключение. Выбор конструкции зависит от применяемых инструкций программы и действий, выполняемых при возникновении ошибки. Конструкции try могут содержать одну или более инструкций, а также быть вложенными друг в друга.
Конструкция try … finally состоит из двух блоков и следующую форму:
Try
// инструкции, выполнение которых может вызвать ошибку
Finally
// инструкции, которые должны быть выполнены даже в случае ошибки
end;
Данная конструкция применяется для выполнения всех необходимых действий перед передачей управления на следующий уровень обработки ошибки или глобальному обработчику. Такими действиями могут быть, например, освобождение оперативной памяти или закрытие файла. Эта конструкция не обрабатывает объект-исключение и не удаляет его, а выполняет действия, которые должны быть произведены даже в случае возникновения ошибки.
Конструкция работает следующим образом: если в любой из инструкций блока try возникает исключение, то управление передается первой инструкции блока finally. Если же исключение не возникло, то последовательно выполняются все инструкции обоих блоков.
На рисунке представлена логика работы оператора try…finally…end.
Конструкции try … except также состоит из двух блоков и имеет следующую форму:
Try
// инструкции, выполнение которых может вызвать ошибку
Except
// инструкции, которые должны быть выполнены в случае ошибки
end;
В отличие от предыдущей инструкции, данная инструкции предназначена для перехвата исключения и предоставляет возможность его обработки.
Конструкция работает следующим образом: если в инструкциях блока try возникает исключение, то управление передается первой инструкции блока except. Если же исключение не возникло, то инструкции блока except не выполняются.
На рисунке представлена логика работы оператора try…except…end.
Схема создания смешанных блоков защищенных операторов и защищенных ресурсов:
{Выделение ресурса}
Try
Try
{операторы, которые могут вызвать ошибку(сгенерировать исключение)}
Except
{Операторы обработки исключений}
end;
Finally
{Освобождение реурса}
end;
При необходимости исключение можно сгенерировать программно. Для этого используется инструкция raise, которая создает объект-исключение – экземпляр класса Exception. Инструкция raise имеет следующий синтаксис:
Raise ClassException.Method;
ClassException является классом исключения, на основе которого создается объект-исключение, а конструктор Method выполняет создание объекта-исключения, а для создания объектов-исключений чаще всего используются методы Create и CreateFmt классов исключений.
ПРИМЕР:
if Length(Edit1.Text)>5 then
Raise Exception.Create('слишком длинная строка');
Сущность рекурсии
Процедура или функция может содержать вызов других процедур или функций. В том числе процедура может вызвать саму себя. Никакого парадокса здесь нет – компьютер лишь последовательно выполняет встретившиеся ему в программе команды и, если встречается вызов процедуры, просто начинает выполнять эту процедуру. Без разницы, какая процедура дала команду это делать.
Пример рекурсивной процедуры:
procedure Rec(a: integer); begin if a>0 then Rec(a-1); writeln(a); end; |
Рассмотрим, что произойдет, если в основной программе поставить вызов, например, вида Rec(3). Ниже представлена блок-схема, показывающая последовательность выполнения операторов.
Рис. 1. Блок схема работы рекурсивной процедуры.
Процедура Rec вызывается с параметром a = 3. В ней содержится вызов процедуры Rec с параметром a = 2. Предыдущий вызов еще не завершился, поэтому можете представить себе, что создается еще одна процедура и до окончания ее работы первая свою работу не заканчивает. Процесс вызова заканчивается, когда параметр a = 0. В этот момент одновременно выполняются 4 экземпляра процедуры. Количество одновременно выполняемых процедур называютглубиной рекурсии.
Четвертая вызванная процедура (Rec(0)) напечатает число 0 и закончит свою работу. После этого управление возвращается к процедуре, которая ее вызвала (Rec(1)) и печатается число 1. И так далее пока не завершатся все процедуры. Результатом исходного вызова будет печать четырех чисел: 0, 1, 2, 3.
Еще один визуальный образ происходящего представлен на рис. 2.
Рис. 2. Выполнение процедуры Rec с параметром 3 состоит из выполнения процедуры Rec с параметром 2 и печати числа 3. В свою очередь выполнение процедуры Rec с параметром 2 состоит из выполнения процедуры Rec с параметром 1 и печати числа 2. И т. д.
В качестве самостоятельного упражнения подумайте, что получится при вызове Rec(4). Также подумайте, что получится при вызове описанной ниже процедуры Rec2(4), где операторы поменялись местами.
procedure Rec2(a: integer); begin writeln(a); if a>0 then Rec2(a-1); end; |
Обратите внимание, что в приведенных примерах рекурсивный вызов стоит внутри условного оператора. Это необходимое условие для того, чтобы рекурсия когда-нибудь закончилась. Также обратите внимание, что сама себя процедура вызывает с другим параметром, не таким, с каким была вызвана она сама. Если в процедуре не используются глобальные переменные, то это также необходимо, чтобы рекурсия не продолжалась до бесконечности.
Сложная рекурсия
Возможна чуть более сложная схема: функция A вызывает функцию B, а та в свою очередь вызывает A. Это называетсясложной рекурсией. При этом оказывается, что описываемая первой процедура должна вызывать еще не описанную. Чтобы это было возможно, требуется использовать опережающее описание.
Пример:
procedure A(n: integer); {Опережающее описание (заголовок) первой процедуры} procedure B(n: integer); {Опережающее описание второй процедуры} procedure A(n: integer); {Полное описание процедуры A} begin writeln(n); B(n-1); end; procedure B(n: integer); {Полное описание процедуры B} begin writeln(n); if n<10 then A(n+2); end; |
Опережающее описание процедуры B позволяет вызывать ее из процедуры A. Опережающее описание процедуры A в данном примере не требуется и добавлено из эстетических соображений.
Если обычную рекурсию можно уподобить уроборосу (рис. 3), то образ сложной рекурсии можно почерпнуть из известного детского стихотворения, где «Волки с перепуга, скушали друг друга». Представьте себе двух съевших друг друга волков, и вы поймете сложную рекурсию.
Рис. 3. Уроборос – змей, пожирающий свой хвост. Рисунок из алхимического трактата «Synosius» Теодора Пелеканоса (1478г).
Рис. 4. Сложная рекурсия.
Дата добавления: 2016-07-05; просмотров: 2423;