Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
КЛ_Середовище.doc
Скачиваний:
0
Добавлен:
01.04.2025
Размер:
1.07 Mб
Скачать
    1. Модель виняткових ситуацій у Delphi

Модель виняткових ситуацій у Object Pascal є непоновлюваної(non-resumable). При виникненні виняткової ситуації Ви вже не зможете повернутися в крапку, де вона виникла, для продовження виконання програми (це дозволяє зробити поновлювана(resumable) модель). Непоновлювані виняткові ситуації руйнують стек, оскільки вони сканують його в пошуках оброблювача; у поновлюваній моделі необхідно зберігати стек, стан регістрів процесора в крапці виникнення помилки і виконувати пошук оброблювача і його виконання в окремому стеці. Поновлювану систему обробки виняткових ситуацій набагато сутужніше створити і застосовувати, ніж непоновлювану.

      1. Синтаксис обробки виняткових ситуацій

Тепер, коли ми розглянули, що таке виняткові ситуації, давайте дамо ясну картину, як вони застосовуються. Нове ключове слово, додане в мову Object Pascal - try. Воно використовується для позначення першої частини захищеної ділянки коду. Існує два типи захищених ділянок:

  • try..except

  • try..finally

Перший тип використовується для обробки виняткових ситуацій. Його синтаксис:

try

Statement 1;

Statement 2;

...

except

on Exception1 do Statement;

on Exception2 do Statement;

...

else

Statements; {default exception-handler}

end;

Для впевненості в тім, що ресурси, зайняті вашим додатком, звільняться в будь-якому випадку, Ви можете використовувати конструкцію другого типу. Код, розташований у частині finally, виконується в будь-якому випадку, навіть якщо виникає виняткова ситуація. Відповідний синтаксис:

try

Statement1;

Statement2;

...

finally

Statements; { These statements always execute }

end;    

    1. Приклади обробки виняткових ситуацій

Нижче приведені процедури 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 New');

end;

Якщо в A помістити виклик New замість 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 можна використовувати усередині оброблювача виняткової ситуації.