Студопедия

Главная страница Случайная страница

Разделы сайта

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






Примеры обработки исключительных ситуаций






 

Ниже приведены процедуры A, B и C, обсуждавшиеся ранее, воплощенные в новом синтаксисе Object Pascal:

 

type

ESampleError = class(Exception);

var

ErrorCondition: Boolean;

 

procedure C;

begin

writeln('Enter C');

if (ErrorCondition) then

begin

writeln('Raising exception in C');

raise ESampleError.Create('Error! ');

end;

writeln('Exit C');

end;

 

procedure B;

begin

writeln('enter B');

C;

writeln('exit B');

end;

 

procedure A;

begin

writeln('Enter A');

try

writeln('Enter A''s try block');

B;

writeln('After B call');

except

on ESampleError do

writeln('Inside A''s ESampleError handler');

on ESomethingElse do

writeln('Inside A''s ESomethingElse handler');

end;

writeln('Exit A');

end;

 

begin

writeln('begin main');

ErrorCondition: = True;

A;

writeln('end main');

end.

 

При

ErrorCondition: = True;

программа выдаст:

 

begin main

Enter A

Enter A's try block

enter B

Enter C

Raising exception in C

Inside A's ESampleError handler

Exit A

end main

 

Возможно вас удивила декларация типа 'ESampleError = class' вместо '=object'; это еще одно новое расширение языка. Delphi вводит новую модель объектов, доступную через декларацию типа '=class'. Описание новой объектной модели дается в других уроках. Здесь же достаточно сказать, что исключительные ситуации (exceptions) являются классами, частью новой объектной модели.

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

Процедура A помещает часть кода в блок try..except. Первая часть этого блока содержит часть кода, аналогично конструкции begin..end. Эта часть кода завершается ключевым словом except, далее следует один или более обработчиков исключительных ситуаций on xxxx do yyyy, далее может быть включен необязательный блок else, вся конструкция заканчивается end;. В конструкции, назначающей определенную обработку для конкретной исключительной ситуации (on xxxx do yyyy), после резервного слова onуказывается класс исключительной ситуации, а после doследует собственно код обработки данной ошибки. Если возникшая исключительная ситуация подходит по типу к указанному после on, то выполнение программы переходит сюда (на код после do). Исключительная ситуация подходит в том случае, если она того же класса, что указан в on, либо является его потомком. Например, в случае on EFileNotFoundобрабатываться будет ситуация, когда файл не найден. А в случае on EFileIO- все ошибки при работе с файлами, в том числе и предыдущая ситуация. В блоке elseобрабатываются все ошибки, не обработанные до этого.

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

После того, как найден подходящий обработчик ошибки, поиск оканчивается. После выполнения кода обработчика, программа продолжает выполняться с оператора, стоящего после слова endблока try..except(в примере - writeln('Exit A')).

Конструкция try..exceptподходит, если известно, какой тип ошибок нужно обрабатывать в конкретной ситуации. Но что делать, если требуется выполнить некоторые действия в любом случае, произошла ошибка или нет? Это тот случай, когда понадобится конструкция try..finally.

Рассмотрим модифицированную процедуру B:

 

procedure NewB;

var

P: Pointer;

begin

writeln('enter B');

GetMem(P, 1000);

C;

FreeMem(P, 1000);

writeln('exit B');

end;

 

Если C вызывает исключительную ситуацию, то программа уже не возвращается в процедуру B. А что же с теми 1000 байтами памяти, захваченными в B? Строка

 

FreeMem(P, 1000);

 

не выполнится и Вы потеряете кусок памяти. Как это исправить? Нужно ненавязчиво включить процедуру B в процесс, например:

 

procedure NewB;

var

P: Pointer;

begin

writeln('enter NewB');

GetMem(P, 1000);

try

writeln('enter NewB''s try block');

C;

writeln('end of NewB''s try block');

finally

writeln('inside NewB''s finally block');

FreeMem(P, 1000);

end;

writeln('exit NewB');

end;

 

Если в A поместить вызов NewB вместо B, то программа выведет сообщения следующим образом:

 

begin main

Enter A

Enter A's try block

enter NewB

enter NewB's try block

Enter C

Raising exception in C

inside NewB's finally block

Inside A's ESampleError handler

Exit A

end main

 

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

Почему вызов GetMem не помещен внутрь блока try? Этот вызов может окончиться неудачно и вызвать exception EOutOfMemory. Если это произошло, то FreeMem попытается освободить память, которая не была распределена. Когда мы размещаем GetMem вне защищаемого участка, то предполагаем, что B сможет получить нужное количество памяти, а если нет, то более верхняя процедура получит уведомление EOutOfMemory.

А что, если требуется в B распределить 4 области памяти по схеме все-или-ничего? Если первые две попытки удались, а третья провалилась, то как освободить захваченную область память? Можно так:

 

procedure NewB;

var

p, q, r, s: Pointer;

begin

writeln('enter B');

P: = nil;

Q: = nil;

R: = nil;

S: = nil;

try

writeln('enter B''s try block');

GetMem(P, 1000);

GetMem(Q, 1000);

GetMem(R, 1000);

GetMem(S, 1000);

C;

writeln('end of B''s try block');

finally

writeln('inside B''s finally block');

if P < > nil then FreeMem(P, 1000);

if Q < > nil then FreeMem(Q, 1000);

if R < > nil then FreeMem(R, 1000);

if S < > nil then FreeMem(S, 1000);

end;

writeln('exit B');

end;

 

Установив сперва указатели в NIL, далее можно определить, успешно ли прошел вызов GetMem.

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






© 2023 :: MyLektsii.ru :: Мои Лекции
Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав.
Копирование текстов разрешено только с указанием индексируемой ссылки на источник.