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

Лекція 14. Обробка виняткових ситуацій

Коли програма конструюється з роздільних модулів, і, особливо, коли ці модулі знаходяться в незалежно розроблених бібліотеках, обробка помилок повинна бути розділена на дві частини:
  1. генерація інформації про виникнення помилкової ситуації, яка не може бути дозволена локально;
  2. обробка помилок, виявлених в інших місцях.

Фундаментальна ідея полягає в тому, що функція, яка виявила проблему, але не знає як її вирішити, генерує виняток в надії, що викликала її (безпосередньо чи опосередковано) функція зможе вирішити проблему. Функція, яка хоче вирішувати проблеми даного типу, може вказати, що вона перехоплює такі винятки.

Такий стиль обробки помилок краще багатьох традиційних технік. Розглянемо альтернативи. При виявленні проблеми, яка не може бути вирішена локально, функція може:
  1. припинити виконання;
  2. повернути значення, що означає «помилка»;
  3. повернути допустиме значення і залишити програму в ненормальному стані.

Варіант 1 - «припинити виконання» - це те, що відбувається за умовчанням, коли НЕ перехоплюється виняток. Для більшості помилок ми повинні придумати щось краще. Бібліотека, безумовно завершальна виконання, не може використовуватися в програмі, перша вимога до якої - надійність.

Варіант 2 - «повернути значення, що сигналізує про помилку» - не завжди виконаємо, бо часто немає прийнятного значення. Навіть в тих випадках, коли такий підхід застосуємо, він часто незручний, тому що результат кожного виклику повинен перевірятися на помилкове значення. Це може сильно збільшити розмір програми.

Варіант 3 - «повернути допустиме значення і залишити програму в ненормальному стані» - має той недолік, що викликає функція може не помітити, що програма знаходиться в ненормальному стані.

Механізм обробки виключень надає альтернативу традиційним методам в тих випадках, коли вони не є достатніми, чи не елегантні і схильні до помилками. Він надає спосіб явного відділення коду обробки помилок від «звичайного» коду, роблячи таким чином програму більш Новомосковскбельной і краще підходить для різних інструментальних засобів. Механізм обробки виключень надає більш регулярний спосіб обробки помилок, спрощуючи в результаті взаємодія між окремо написаними фрагментами коду.

Треба розуміти, що обробка помилок залишається складним завданням і що механізм обробки виключень - незважаючи на бóБільшу формалізацію, ніж альтернативні методи - відносно менш структурований в порівнянні із засобами мови, що забезпечують локальне управління виконанням. Механізм обробки виключень C ++ надає програмісту засіб обробки помилок в тому місці, де їх найприродніше обробляти при даній структурі системи. Винятки роблять складність обробки помилок більш явною. Однак виключення не є причиною цієї складності.

Механізм обробки виключень можна розглядати як альтернативу механізму повернення з функції. Тому цілком законно використовувати виключення, які не мають ніякого відношення до помилок. Однак початковою метою механізму обробки винятків є обробка помилок і забезпечення стійкості при виникненні помилок.

Обробка виняткових ситуацій забезпечує спосіб передачі управління і інформації в невизначену точку, де було висловлено бажання обробляти ситуації даного типу. Ситуації будь-якого типу можуть збуджуватися (throw) і перехоплюватися (catch), і для функції може бути перераховано безліч ситуацій, які можуть бути в ній порушені.

Реакція буде викликана тільки в разі виконання вираження збудження всередині блоку з контролем або у функціях, викликаних з цього блоку.

Синтаксис блоку з контролем:
try<список реакций>

Вираз збудження має наступний синтаксис:
throw<выражение> ;

При порушенні ситуації (тобто виконання оператора throw) управління передається на реакцію. Тип операнда оператора throw визначає, які реакції можуть перехоплювати дану ситуацію.

Якщо серед реакцій блоку з контролем, не знайдено підходящої реакції, пошук підходящої реакції триває в блоці з контролем, що охоплює даний блок з контролем.

Якщо в програмі не знайшлося відповідної реакції, викликається функція terminate (). Функція terminate () викликає функцію, задану при останньому зверненні до функції set_terminate (). За замовчуванням функція, що викликається з функції terminate (). є abort (). Функція, що викликається функцією terminate (). повинна завершувати виконання програми.

Виняток є об'єктом деякого класу. що є поданням виняткового випадку. Код, який знайшов помилку, генерує об'єкт інструкцій throw. Фрагмент коду висловлює своє бажання обробляти виключення за допомогою інструкції catch. Результатом генерації виключення інструкцією throw є розкручування стека до тих пір, поки не буде виявлений відповідний catch в функції, яка безпосередньо або опосередковано викликала функцію, згенерованого виняток.

Сам факт створення виключення передає інформацію про помилку і її тип. Крім того, виключення може містити додаткову інформацію. Фундаментальною метою техніки обробки винятків є передача інформації для відновлення після виникнень проблеми і для здійснення цього надійним і зручним способом.

Оброблювач буде викликаний, якщо:
  1. Н того ж типу, що і Е;
  2. Н є однозначно доступним публічним базовим класом для Е;
  3. Н і Е є покажчиками, і 1 або 2 виконується для типів, на які вони посилаються;
  4. Н є посиланням, і 1 або 2 виконується для типу, на який посилається Н.

Часто виключення природним чином розбиваються на сімейства. З цього випливає, що спадкування може виявитися корисним для структурування винятків і допомогти при їх обробці. Наприклад, виключення для математичної бібліотеки можна організувати в такий спосіб.

class MathErr <.>; class Overflow. public MathErr <.>; class Underflow. public MathErr <.>; class ZeroDivision. public MathErr <.>;

// Переповнення зверху // Переповнення знизу // Поділ на 0

Це дозволяє нам обробляти виключення будь-якого класу, похідного від MathErr. не піклуючись про те, яке саме виключення виникло.

// Обробка виключення Overflow і всіх похідних від нього винятків // Обробка будь-якого винятку MathErr. яка не є Overflow

Організація винятків у вигляді ієрархій може мати велике значення для надійності коду. При введенні нового виключення в математичну бібліотеку кожен фрагмент коду, який намагається обробляти всі математичні виключення, підлягав би модифікації. Це вимагає величезних витрат праці і в загальному випадку неможливо.

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

// Можливо повністю обробити помилку? // Робимо, що можливо // Повторна генерація виключення

Факт повторного генерації відзначається відсутністю операнда у throw. Якщо здійснена спроба повторної генерації при відсутності виключення, буде викликана функція terminate ().

Повторно генерується виняток є вихідним винятком, тобто навіть якщо обробник працював з об'єктом базового класу. охоплює блок отримає спочатку згенерований об'єкт похідного класу.

Оскільки виключення похідних класів можуть бути перехоплені обработчиками винятків базових класів, порядок, в якому записані обробники в інструкції try. має велике значення. Обробники перевіряються в порядку їх запису.

try catch (std :: ios_base :: failure) catch (std :: exception) catch (.)

// Обробка помилок в потоці введення / виведення // Обробка виключень стандартної бібліотеки // Обробка всіх інших винятків

Винятки надають спосіб вирішити проблему, як повідомити про помилку з конструктора. Оскільки конструктор не повертає значення, яке викликає функція могла б перевірити, традиційними (тобто без використання обробки винятків) альтернативами залишаються наступні.
  1. Повернути об'єкт в «неправильному» стані і покладатися на те, що користувач перевірить його стан.
  2. Присвоїти значення нелокальної змінної для вказівки на неуспішне створення об'єкта і покладатися на те, що користувач його перевірить.
  3. Чи не здійснювати ніякої ініціалізації в конструкторі і покладатися на те, що користувач викличе функцію ініціалізації (яку ще треба написати!) До першого використання об'єкта.
  4. Помітити об'єкт як неініціалізованих і при першому виклику функції-члена класу для цього об'єкта здійснити ініціалізацію (така функція може повернути повідомлення про помилку в разі неуспішної ініціалізації, але користувач знову-таки повинен перевіряти повертається функцією значення).

Винятки дозволяють передати інформацію про неуспішною ініціалізації з конструктора. Наприклад, клас Vector міг би захиститися від запиту занадто великої кількості пам'яті, генеруючи відповідне виключення. class Vector <. public. class Size <.>; Vector (int n = 0); .>; Vector :: Vector (int n) MAX_SIZE) throw Size (); .>

Код, що створює вектора, тепер може перехопити помилку Vector :: Size і спробувати зробити щось осмислене.

Якщо виняток очікується і перехоплено таким чином, що не робить негативного впливу на поведінку програми, чому це може вважатися помилкою? Механізми обробки винятків можна розглядати як ще одну керуючу структуру.

// Функція Get генерує виняток Empty. якщо чергу порожня

Обробка винятків є менш структурованим механізмом, ніж локальні керуючі структури, до того ж обробка винятків часто менш ефективна, якщо виключення дійсно генерується. Тому виключення слід використовувати в тих випадках, коли традиційні керуючі структури є неелегантно рішенням, або ними неможливо скористатися.

Непомірне використання винятків веде до незрозумілого коду. Зазвичай слід дотримуватися точки зору «обробка винятків є обробкою помилок». При такому підході код виявляється зрозумілим чином розділений на дві частини: звичайний код і код обробки помилок.

Передбачається, що функція, оголошена без специфікації винятків, може згенерувати будь виняток.
int g (int n);

Функцію, яка не генерує винятків, можна оголосити з порожнім списком специфікацій винятків.
int h (int n) throw ();

Модифікуємо клас Stack (з лекції 11) так, щоб при переповненні стека і спробі взяти елемент з пустого стека генерувалися відповідні винятки.
#include class Stack; class StackEmpty Stack * GetPtr () >; class StackFull Stack * GetPtr () int GetValue () >; class Stack ; int stack [SIZE]; int * cur; public. Stack ()