Виняткові ситуації

При роботі програми можуть виникати різного родупомилки: Переповнення, розподіл на нуль, спроба відкрити неіснуючий файл і т.п. При виникненні такихвиняткових ситуаційпрограма генерує переривання, званевинятком. В результаті виконання подальших обчислень припиняється.Виняток– Е.то об’єкт спеціального виду, що характеризує виниклу в програмі особливу ситуацію. Він може також містити у вигляді параметрів деяку уточнюючу інформацію. Особливістю винятків є те, що це суто тимчасові об’єкти. Як тільки вони оброблені якимось обробником, вони руйнуються.
Якщовиключенняне перехоплено ніде в програмі, то воно автоматично обробляється методомTApplication.HandleException. Він забезпечує стандартну реакцію програми на більшістьвинятків– Видачу користувачеві короткої інформації у вікні повідомлень і знищення екземпляра виключення. На рис. 3 наведено приклад такого стандартного повідомлення для випадку цілочисельного ділення на нуль.
Рис. 3
Якщо робота по налагодженню програми ведеться в середовищіDelphi, То привинятках, Подібних вказаною на мал. 3, можуть з’являтися повідомленнявідладчика Delphi, Вид яких наведено на рис.4.
Рис. 4
При цьому на екрані відкривається вікно Редактора Коду на рядку, при виконанні якої відбулосявиключення. Необхідно клацнути на ОК, а потім слід переглянути змінні і з’ясувати причину виключення. Після цього можливі наступні дії:
1) натискання– Припинення виконання додатку;
2) натисканняабо виконання команди Run / Run – продовження роботи додатку;
3) натисканняабо– Продовження налагодження по кроках.
При необхідності можна відключити появу повідомлень відладчика. Для цього треба виконати команду Tools / Debugger Options, в діалоговому вікні вибрати сторінку Language Exceptions і на ній вимкнути опцію Stop On Delphi Exceptions.
Якщо не вжити відповідних заходів у разі створення виключень, то до неприємностей припинення обчислень можуть додатися ще неприємності, пов’язані з так званою витоком ресурсів. Під цим маються на увазі втрати динамічно розподіляє пам’яті, незакриті файли, не знищені тимчасові файли на диску та інший інформаційний “сміття”. Наприклад, нехай виконується деяка програма, в якій є наступні оператори:
AssignFile(F,a.tmp);
Rewrite (F); 
New(P);

Erase(F); 
Dispose (P);

У програмі відкривається тимчасовий файл з ім’ям a.tmp, щоб зберігати в ньому результати проміжних обчислень. В кінці програми файл повинен бути знищений процедурою Erase. Крім того, процедурою New динамічно виділяється деякий об’єм оперативної пам’яті. Пам’ять буде звільнятися після її використання процедурою Dispose. Якщо в проміжних операторах, помічених вище точками, виникне виключення, то обчислення перервуться і процедури Erase і Dispose не будуть виконані. В результаті пам’ять, виділена процедурою New, залишиться недоступною, а на диску збережеться тимчасовий і вже непотрібний файл a.tmp.
Уникнути подібного роду неприємностей можна кількома шляхами:
1. Не допускати виключень, виробляючи попередню перевірку кожної операції, здатної призвести до виключних ситуацій.
2. Захищати код очищення інформаційного “сміття” за допомогою програмного блоку try … finally.
3. Обробляти виключення в блоках try … except.
Перший із зазначених шляхів боротьби з винятками передбачає введення перевірки перед виконанням кожної операції, здатної призвести до виключних ситуацій. Наприклад, перед поділом двох змінних А і В, з яких друга може виявитися нулем, треба перевірити значення В оператором
if (В = 0) then …

і виконати необхідні дії, якщо змінна В виявиться рівною нулю. Аналогічно можна перевіряти можливість переповнення при діленні, множенні і інших арифметичних операціях, перевіряти наявність достатнього місця в динамічно розподіляє пам’яті при виділенні нової галузі або створення нового об’єкта, перевіряти помилки файлового вводу виводу і т. д. При використанні бібліотечних функцій можна перед кожним зверненням до функції або процедури проводити перевірку допустимості всіх її аргументів.
Перевагою розглянутого підходу до усунення виключень є майже “бессбойную” робота програми (якщо, звичайно, дійсно перевіряється кожна операція). Слово “бессбойную” тут недарма поставлено в лапки. Складні програми, на жаль, завжди можуть містити помилки і можливості збою. Навіть ретельна попередня перевірка всіх операцій не здатна це усунути.
При безперечних перевагах розглянутого підходу у нього є ряд недоліків. Основний з них – збільшення розміру програми (особливо, якщо вона проводить багато складних обчислень) за рахунок численних операторів перевірки та уповільнення її роботи. До того ж при використанні бібліотечних функцій і процедур, реалізація яких невідома, не завжди можна бути впевненим, що перевірки гарантують їх бессбойную роботу.
В даний час перевірку окремих операцій і функцій на можливість появи виняткових ситуацій треба визнати застарілим підходом, що призводить до невиправданого ускладнення програми і зниження її ефективності. Сучасний підхід полягає в обробці виключень.
Програміст повинен прийняти всі мислимі заходи, щоб ні за яких помилках користувача і ні за яких поєднаннях даних додаток не закінчувалося б аварійно. Але якщо все-таки аварійне завершення відбувається, необхідна повна очистка “сміття” – видалення тимчасових файлів, звільнення пам’яті, розрив зв’язків з базами даних і т. д.
Другий спосіб боротьби з винятками полягає у використанні програмного блоку try … finally. Блок, що містить сукупність операторів, здатних призвести до виключення, можна оформити наступним чином:
try
<Оператори, здатні призвести до виключення і до появи "сміття">
finally
<Оператори, що виконуються в будь-якому випадку і виробляють зачистку "сміття">
end;

У цьому випадку оператори в розділі finally будуть виконуватися завжди, незалежно від того, було чи не було виключення. Після виконання цих операторів обчислення, як і раніше, перериваються. Якщо в блоці try були відкриті якісь тимчасові файли, динамічно виділялася пам’ять під тимчасові об’єкти, виконувалися з’єднання з якимись базами даних, а після всього цього сталася помилка, що викликала виняток, то в блоці finally можна прибрати весь “сміття”: видалити непотрібні тимчасові файли, звільнити пам’ять від тимчасових об’єктів, розірвати зв’язок з базою даних.
На жаль, залишаються інші з розглянутих проблем: необхідність вжити якихось заходів для подальшої нормальної роботи програми при генерації виключення, а також необхідність повідомити користувача про бажаних діях з його боку. Вирішити ці проблеми в даному випадку неможливо, оскільки при виконанні операторів розділу finally програма не знає, чи відбулося виключення і якщо відбулося, то яке саме.
Розглянутий спосіб використання блоку try … finally дозволяє захистити ресурси програми всередині блоку. Існує також спосіб глобального виділення та ініціалізації ресурсів, з захищеною їх очищенням. Він полягає у використанні двох спеціальних включаються в модуль розділів initialization і finalization. Розділ initialization, виконуваний один раз при першому згадуванні в програмі даного модуля, можна використовувати для одноразового виділення необхідних глобальних ресурсів. А розділ finalization, гарантовано виконуваний в кінці роботи програми незалежно від наявності або відсутності виключень, можна використовувати для очищення цих та інших ресурсів, затребуваних при роботі програми. Наприклад, якщо в процесі роботи програми тимчасові файли з розширеннями. Tmp можуть створюватися в каталозі, ім’я якого записано у змінній sdirtmp, то гарантовано видалити їх при завершенні роботи програми можна наступними операторами:
var 
sSR:TSearchRec; F:File;
ires:integer;
….
initialization
….
finalization 
ires := FindFirst(sdirtmp+*.tmp, faAnyFile, sSR); 
while ires=0 do 
begin 
AssignFile(F, sdirtmp+sSR.name); 
Erase(F);
ires := FindNext(sSR) 
end;

end.

У цьому фрагменті програми за допомогою процедури Erase з каталогу, ім’я якого записано в змінної sdirtmp, видаляються всі файли з розширенням. Tmp. Пошук цих файлів здійснюється функціями FindFirst і FindNext.
У розділі finalization можна визначити, чи завершується програма нормально або в результаті генерації виключення, перевіривши функцію ExceptAddr.
Слід врахувати, що включення в модуль розділу finalization можливе лише за наявності в ньому розділу initialization. Навіть якщо він не потрібний, потрібно включити в програму хоча б порожній розділ initialization, якщо передбачається використовувати очистку ресурсів в розділі finalization.
Найбільш кардинальним способом боротьби з винятками є третій з вищезазначених – обробка виключень за допомогою блоків try … except. Синтаксис цих блоків наступний:
try
<Виконуваний код>
except
<Код, виконуваний в разі помилки>;
end;

Оператори розділу except виконуються тільки у разі генерації виключення в операторах блоку try. В результаті при виникненні виключення з’являється можливість почати які-небудь дії: сповістити користувача про виниклу проблему і підказати йому шляхи її вирішення, прийняти якісь заходи до виправлення помилки (наприклад, при діленні на нуль заслати в результат дуже велике число відповідного знака) і т.д.
Важливим є те, що в розділі except можна визначити тип згенерованого виключення і диференційовано реагувати на різні виняткові ситуації. Це робиться за допомогою оператора:
on <клас винятку> do <оператор>;

У мові Object Pascalвиняткові ситуаціїявляють собою об’єкти, що містять інформацію, що ідентифікує помилку і місце її виникнення. Всередині розділу except створюються оброблювачі особливих ситуацій для класів виняткових ситуацій. Оброблювач особливої ​​ситуації має наступний формат:
try
<Виконуваний код>
except
on E: ESomeException do <обробник виключення>;
end;

Для обробки особливої ​​ситуації Delphi надає можливість створення тимчасових об’єктів особливої ​​ситуації (Е), які можуть використовуватися в обробнику виключення. Тимчасовий об’єкт особливої ​​ситуації має той же тип, що і об’єкт винятку, який він позначає (ESomeException).
Існують зумовлені класи виняткових ситуацій для обробки стандартних помилок, таких як нестача пам’яті, розподіл на нуль, числове переповнення, помилки введення-виведення та інші. Деякі з них наступні:
1. EMathError – клас-предок винятків, що трапляються при виконанні операцій з плаваючою точкою;
EInvalidArgument – значення параметра виходить за діапазон значень
EInvalidOp – передача математичному сопроцессору помилковою конструкції;
EOverflow – переповнення розрядів при роботі із занадто великими величинами;
EUnderflow – втрата розрядів при роботі із занадто малими величинами;
EZeroDivide – поділ на нуль.
2. EIntError – клас-предок винятків, що трапляються при виконанні цілочисельних операцій;
EDivByZero – поділ на нуль;
EIntOverflow – виконання операцій, що приводять до переповнення цілих змінних;
ERangeError – значення цілочисельного виразу виходить за межі встановленого цілочисельного типу. Спроба звернення до елементу масиву за індексом, який виходить за межі масиву.
3. EListError – звернення до елемента списку (String, List, TStringList) за індексом, який виходить за межі допустимих значень.
4. EInOutError – помилки при операціях з файловою системою. Код помилки повертається в локальній змінній ErrorCode, яка може приймати наступні значення:
2 – файл не знайдений;
3 – неправильне ім’я файлу;
4 – занадто багато відкрито файлів;
5 – відмова в доступі;
100 – кінець файлу;
101 – диск сповнений;
106 – невірна операція введення.

5. EConvertError – помилки перетворення типів (простих, об’єктних).
Оператори on … do дозволяють проводити вибіркову обробку різнихвинятків. Приклад такої обробки:
var A : shortint;

try

З: = StrToInt (Editl.text); A: = В div С;
except 
on EConvertError do 
MessageDlg (Ви ввели помилкове число; повторіть введення, mtWarning, [mbOk], 0);
on EDivByZero do 
MessageDlg (Ви ввели нуль; повторіть введення, mtWarning, [mbOk], 0);
on EIntOverflow do
if (B*C) >= 0 then A := 32767 else A := -32767;

end;

У цьому прикладі виконуються читання цілого числа, введеного користувачем у вікно редагування Editl, і поділ на нього змінної В. Якщо користувач ввів не ціле число (наприклад, помилково натиснув не цифру, а якийсь буквений символ), то при виконанні функції StrToInt виникне виключення класу EConvertError. Відповідний обробник виключення повідомляє користувачеві про зроблену помилку і радить повторити введення. Аналогічна реакція слід на введення користувачем нуля (клас виключення EDivByZero). Якщо ж при розподілі виникає цілочисельне переповнення (клас виключення Elnt-Overflow), то результату присвоюється максимальне позитивне або негативне значення.
Деяківиключеннямають додаткові поля (властивості), уточнюючі вид помилки. Наприклад, це відноситься довиключенню файлового введення / виводу EInOutError, Яке має властивість errorcode типу integer. Ця властивість містить стандартний для Object Pascal номер помилки файлового введення / виводу.
Щоб скористатися полями винятків, оператор on треба записувати у вигляді
on <ім'я>: <клас винятку> do
<Оператори з конструкціями <ім'я>. <Ім'я поля >>;

Міститься в цьому операторі <ім'я> носить суто локальний характер, ніде раніше визначатися не повинно і вводиться тільки для того, щоб можна було послатися на поле по імені об’єкта виключення.
Приклад використання полів при операціях файлового вводу / виводу (мається на увазі, що у змінній filename типу string зберігається ім’я оброблюваного файлу):
on IO: EInOutError do 
begin 
Case IO.errorcode of
2: s: = Файл + s + не знайдений;
3: s: = Помилкове ім’я файлу + s +;
4: s: = Занадто багато відкритих файлів;
5: s: = Файл + s + не доступний;
100: s: = Досягнуто кінець файлу + s + “”;
101: s: = Диск переповнено при роботі з файлом + s +;
106: s: = Помилка введення при роботі з файлом + s +;
end;
MessageDlg(s, mtWarning, [mbOk], 0); 
end;

Наведений код аналізує полевиключенняі видає повідомлення про вигляді помилки.
Крім операторів on, оброблювальних задані типивинятків, В розділ except може бути включений оператор else, в якому виконується обробка всіх не перехоплених ранішевинятків, Тобто відбувається обробка за замовчуванням. Наприклад:
except 
on . . . ; 
on . . . ;
else <обробник всіх не перехоплених раніше подій>;
end;

Оператор else повинен йти останнім, тому що в противному випадку всі обробники, записані після нього, працювати не будуть, оскільки всі винятки вже будуть перехоплені. Усередині обробника за замовчуванням можна отримати доступ до об’єкта виключення, скориставшись функцією ExceptObject.
У розділexceptможуть включатися або тільки оператори on, або тільки якісь інші оператори. Змішання операторів on з іншими не допускається.
Розділ except може включатися спільно з описаним раніше розділом finally. Наприклад, можна організувати блоки try наступним чином:
try

try

finally

end; 
except

end;

Блоки try … exceptможуть бути вкладеними явним чи неявним чином. Прикладом неявній вкладеності є блок try … except, в якому серед операторів розділу try маються виклики функцій або процедур, які мають свої власні блоки try … except. Розглянемо послідовність обробки виключень в цих випадках. При генерації виключення спочатку шукається відповідний йому обробник on в тому блоці try … except, в якому створилася виняткова ситуація. Якщо відповідний обробник не знайдений, пошук ведеться в обрамляючому блоці try … except (за наявності явним чином вкладених блоків) і т.д. Якщо в даній функції або процедурі обробник не знайдений або взагалі в ній відсутні блокиtry…except, То пошук переходить на наступний рівень – в блок, з якого була викликана дана функція або процедура. Цей пошук триває по всіх рівнях. І тільки якщо він закінчився безрезультатно, виконується стандартна обробка виключення, що полягає, як уже було сказано, у видачі користувачеві повідомлення про тип виключення.
Як тільки оператор on, відповідний даному виключенню, знайдений і виконаний, об’єкт виключення руйнується і керування передається оператору, наступному за відповідним блоком try … except.
Можливий також варіант, коли в самомуобробнику виключенняв процесі обробки виникла виняткова ситуація. У цьому випадку обробка переривається, колишнє виключення руйнується і генерується нове виняток. Його обробник шукається в блоці try … except, зовнішньому по відношенню до того, в якому виникло новевиключення.
Наприклад, для створення стійкого до помилок додатки, записуючого дані у файл, можна використовувати наступний код:
begin
assignfile(f, data.dan); 
try 
append(f); 
try 
writeln (f,s); 
finally
closefile(f) ; 
end; 
except 
on E: EInOutError do 
if E.ErrorCode = 2 then 
if MessageDlg (Файл не знайдений. Створити його?, mtError, [mbYes, mbNo], 0) = mrYes
then FileCreate(data.dan); 
end; 
end;

У даному прикладі внутрішній блок try … finally використовується для того, щоб файл був закритий в будь-якому випадку, тобто незалежно від того, була помилка чи ні.
Зовнішній блок try … except використовується для обробки виняткової ситуації, яка може відбутися в програмі. Після закриття файлу у внутрішньому блоці finally блок except використовує тимчасовий об’єкт Е для визначення типу сталася помилки вводу / виводу.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*