Такі події призводять до завершення програми з системним повідомленням про помилку. Механізм винятків дозволяє відновлювати програму і продовжувати її виконання.
Винятки C ++ не підтримують обробку асинхронних подій (помилки обладнання або обробку переривань), а призначені тільки для подій, які відбуваються в результаті роботи самої програми і вказуються явно.
Винятки поділяють обчислювальний процес на дві частини - виявлення аварійної ситуації та її обробка.
· Функції, що виявила помилку, може бути невідомо, як її виправити, а використовує цю функцію код не здатний визначити місце виникнення. Актуально при використанні бібліотечних функцій і програм, що складаються з багатьох модулів,
· Для передачі інформації про помилку в зухвалу функцію не потрібно застосовувати повертається значення, параметри або глобальні змінні.
Порядок обробки виняткової ситуації:
· З'являється помилка, і функція, де вона виникла, генерує (породжує) виняток,
· Виконання поточного блоку припиняється, відбувається пошук відповідного обробника і передача йому управління,
· Якщо він не знайдений, викликається стандартна функція terminate, яка в свою чергу викликає функцію abort, аварійно завершальну поточний процес. Можна встановити власну функцію завершення процесу.
Стек викликів - послідовність викликаних, але ще не завершилися функцій.
Розкручування стека - процес звільнення пам'яті з-під локальних змінних і повернення управління викликає функції. При завершенні функції відбувається розкручування стека.
Так як управління може бути повернуто в точку виклику, тільки після завершення функції, то після того, як виняток було зафіксовано, виконання не може бути продовжено з точки генерації виключення.
Синтаксис контролюючого блоку (коду, в якому може генеруватися виняток):
Належать try-блоку також вважаються функції прямо чи опосередковано викликані з нього.
Синтаксис генерації виключення:
Параметр (вираз) передає інформацію про виключення його обробнику. Параметр може бути константою, змінною або об'єктом. Тип параметра (вираження) визначає тип породжується виключення.
Як правило, виключення генерується в функціях, вкладених в try-блок. Не завжди таке виключення може бути відразу правильно оброблено. В цьому випадку використовуються вкладені контрольовані блоки, і виключення передається на більш високий рівень за допомогою ключового слова throw без параметрів.
Синтаксис обробника (три форми запису):
1. Застосовується, коли ім'я виключення використовується в тілі обробника для виконання будь-яких дій (наприклад, виведення інформації про виключення).
catch (тип ім'я), де параметр тип - тип виключення.
2. Інформація про ім'я виключення в обробнику не використовується, грає роль тільки його тип.
3. Оброблювач перехоплює все виключення.
Обробники проглядаються в тому порядку, в якому вони записані, тому обробник такого типу слід поміщати після всіх інших.
Оброблювач повинен розташовуватися безпосередньо за try-блоком. Можна записати один або кілька обробників відповідно до типів оброблюваних винятків.
// Обробка винятків типу int
catch (const char *)
// Обробка винятків типу const char *
// Обробка винятків класу Overflow
// Обробка всіх необслужених винятків
Після обробки виключення управління передається першому оператору, що знаходиться безпосередньо за обработчиками винятків. Якщо виключення в try-блоці не було згенеровано, управління, минаючи код всіх обробників, передається в цю ж точку.
Після генерації виключення з допомогою throw функції виконавчої бібліотеки C ++ виконують:
· Створення копії параметра throw у вигляді статичного об'єкта (існує до тих пір, поки винятку не буде оброблено);
· Розкручування стека в пошуках підходящого обробника (виклик деструкторів локальних об'єктів, що виходять з області дії). Все обробники на кожному рівні проглядаються послідовно, від внутрішнього блоку до зовнішнього;
· Передачу об'єкта і управління знайденому оброблювачу.
Оброблювач вважається знайденим, якщо тип об'єкта, зазначеного після throw:
· Збігається з параметром, зазначеним в параметрі catch (може бути записаний у формі Т, const Т, Т або const Т, де Т-тип виключення);
· Є похідним від наданого в секції catch (якщо спадкування вироблялося з ключем доступу public). Тому обробники похідних класів слід розміщувати до обробників базових (в іншому випадку їм ніколи не буде передано управління).
· Є покажчиком, який може бути перетворений за стандартними правилами перетворення покажчиків до типу покажчика в параметрі catch. Покажчик типу void слід розміщувати після обробників покажчиків конкретного типу (він приховує покажчик будь-якого іншого типу).
// Клас, що інформує про своє створення і знищення
ifstream ifs ( "\\ INVALID \\ FILE \\ NAME"); // Відкриваємо файл
Результати виконання програми:
Входимо в try-блок
Викликаний оброблювач const char *, виняток - Помилка при відкритті файлу
Після породження виключення був викликаний деструктор локального об'єкта, хоча управління з функції f1 було передано оброблювачу, що знаходиться в функції main. Повідомлення «Виходимо з try-блоку» не було виведено.
Механізм винятків дозволяє коректно знищувати об'єкти при виникненні помилкових ситуацій. Тому виділення і звільнення ресурсів корисно оформляти у вигляді класів конструктор яких виділяє ресурс, а деструктор звільняє.
Прикладом є клас для роботи з файлом. Конструктор класу відкриває файл, а деструктор - закриває. При виникненні помилки файл буде коректно закритий, і інформація не буде загублена.
Виняток може бути як стандартного, так і певного користувачем типу. При цьому немає необхідності визначати цей тип глобально - досить, щоб він був відомий в точці породження виключення і в точці його обробки.
Клас для подання виключення можна описати всередині класу, при роботі з яким воно може виникати. Конструктор копіювання цього класу повинен бути оголошений як public, інакше буде неможливо створити копію об'єкта при генерації виключення (конструктор копіювання, створюваний за замовчуванням, має специфікатор public).
Винятки у функціях
У заголовку функції можна задати список винятків, які вона може прямо або побічно породжувати. Типи виключень перераховуються в дужках через кому після ключового слова throw, розташованого за списком параметрів функції:
void fl () throw (int, const char *)* Тело функции */>
Функція fl повинна генерувати виключення тільки типів int і const char *, f2 - тільки виключення типу покажчика на клас Oops або похідних від нього класів.
Якщо ключове слово throw не вказано, функція може генерувати будь-виняток. Порожній список означає, що функція не повинна породжувати винятків:
// Тіло функції, не породжує винятків
Тема є інтерфейсом функції, тому зазначення в ньому списку винятків дає інформацію для її використання, а також гарантію, що при виникненні непередбаченої виключення ця ситуація буде виявлена (функція може прямо або побічно породити виняток, яке не описано в її інтерфейсі).
Виникнення непередбаченого виключення призводить до виклику стандартної функції unexpected, яка за замовчуванням викликає функцію terminate (рисунок 2.3). За допомогою функції set_unexpected можна встановити власну функцію, яка буде викликатися замість terminate і визначати дію програми при виникненні непередбаченої виняткової ситуації.
Функція terminate за замовчуванням викликає функцію abort, яка завершує виконання програми. За допомогою функції set_terminate можна встановити власну функцію, яка буде викликатися замість abort і визначати спосіб завершення програми. Функції set_unexpected і set_terminate описані в заголовному файлі
Малюнок 2.3 - Алгоритм обробки виключення.
Винятки не входять до прототип функції. При перевизначенні в похідному класі віртуальної функції можна задавати список винятків, такий же або більш обмежений, ніж у відповідній функції базового класу.