Урок №190. Обробка винятків. Оператори throw, try і catch

  Юрій  | 

  Оновл. 26 Бер 2021  | 

 203

На попередньому уроці ми говорили про необхідність і користь винятків. Винятки в мові C++ реалізовані за допомогою 3-х ключових слів, які працюють в зв’язці один з одним: throw, try і catch.

Генерація винятків

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

У мові C++ оператор throw використовується для сигналізування про виникнення винятку або помилки (аналогія того, коли свистить арбітр). Сигналізування про те, що стався виняток, називається генерацією винятку (або “викиданням винятку”).

Для використання оператора throw застосовується ключове слово throw, а за ним вказується значення будь-якого типу даних, яке ви хочете задіяти, щоб сигналізувати про помилку. Як правило, цим значенням є код помилки, опис проблеми або спеціальний клас-виняток. Наприклад:

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

Пошук винятків

Генерація винятків — це лише одна частина процесу обробки винятків. Повернемося до нашої аналогії з баскетболом: як тільки просвистів арбітр, що відбувається далі? Гравці зупиняються, і гра тимчасово припиняється. Звичайний хід гри порушений.

У мові C++ ми використовуємо ключове слово try для визначення блоку стейтментів (так званого «блоку try»). Блок try діє як спостерігач в пошуках винятків, які були викинуті будь-яким з операторів в цьому ж блоці try, наприклад:

Зверніть увагу, блок try не визначає, як ми оброблятимемо виняток. Він просто повідомляє компілятору: «Ей, якщо будь-який зі стейтментів всередині цього блоку try згенерує виняток — злови його!».

Обробка винятків

Поки арбітр не оголосить штрафний кидок, і поки цей штрафний кидок не буде виконано, гра не відновиться. Іншими словами, штрафний кидок повинен бути “оброблений” до відновлення гри.

Фактично, обробка винятків — це робота блоку(ів) catch. Ключове слово catch використовується для визначення блоку коду (так званого «блоку catch»), який обробляє винятки певного типу даних.

Ось приклад блоку catch, який обробляє (ловить) винятки типу int:

Блоки try і catch працюють разом. Блок try виявляє будь-які винятки, які були викинуті в ньому, і направляє їх до відповідного блоку catch для обробки. Блок try повинен мати, принаймні, один блок catch, який знаходиться відразу ж за ним, але також може мати і кілька блоків catch, розміщених послідовно (один за одним).

Як тільки виняток було спіймано блоком try і направлено в блок catch для обробки, він вважається обробленим (після виконання коду блоку catch), і програма відновлює своє виконання.

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

Як і у випадку з функціями, якщо параметр не використовується в блоці catch, то ім’я змінної можна не вказувати:

Це запобіжить виведенню попереджень від компілятора про невикористані змінні.

Використання throw, try і catch разом

Ось повна програма, яка використовує throw, try і кілька блоків catch:

Результат виконання програми:

We caught an int exception with value -1
Continuing our way!

Оператор throw використовується для генерації винятку -1 типу int. Потім блок try виявляє оператор throw і переміщує його до відповідного блоку catch, який обробляє винятки типу int. Блок catch типу int і виводить відповідне повідомлення про помилку.

Після обробки винятку, програма продовжує своє виконання і виводить на екран Continuing our way!.

Резюмуємо

Обробка винятків, насправді, досить-таки проста, і все, що вам потрібно запам’ятати, розміщено в наступних двох абзацах:

   При викиданні винятку (оператор throw), точка виконання програми негайно переходить до найближчого блоку try. Якщо будь-який з обробників catch, прикріплених до блоку try, обробляє цей тип винятку, то точка виконання переходить в цей обробник і, після виконання коду блоку catch, виняток вважається обробленим.

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

Зверніть увагу, компілятор не виконує неявні конвертації при зіставленні винятків з блоками catch! Наприклад, виняток типу char не буде оброблятися блоком catch типу int, а виняток типу int, в свою чергу, не буде оброблятися блоком catch типу float.

Це дійсно все, що вам потрібно запам’ятати.

Винятки обробляються негайно

Ось маленька програма, яка демонструє, що винятки обробляються негайно:

Розглянемо виконання цієї програми покроково:

   Оператор throw — це перший оператор, який виконується. Він призводить до генерації винятку типу double.

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

   Потім перевіряються обробники catch на відповідність типу даних. Оскільки у нас виняток типу double, то компілятор шукає обробник catch типу double. У нас такий є, тому він і виконується.

Відповідно, результат виконання програми:

We caught a double of value: 7.4

Зверніть увагу, рядок This never prints ніколи не виводиться, тому що генерація винятку змусила точку виконання програми негайно перейти до обробника винятків типу double.

Ще один приклад

Розглянемо більш популярний приклад:

Тут ми просимо користувача ввести число. Якщо користувач ввів додатне число, то стейтмент if не виконується, виняток не генерується, і користувач отримує квадратний корінь з числа. Оскільки виняток не генерується, то код всередині блоку catch ніколи не виконується.

Результат:

Enter a number: 16
The sqrt of 16 is 4

Якщо ж користувач ввів від’ємне число, то генерується виняток типу const char*. Оскільки ми вже знаходимося в блоці try, то компілятор шукає відповідний обробник catch типу const char*, і точка виконання негайно переміщується в цей блок.

Результат:

Enter a number: -3
Error: Can not take sqrt of negative number

Що зазвичай роблять блоки catch?

Якщо виняток направлено в блок catch, то він вважається «обробленим», навіть якщо блок catch порожній. Однак, як правило, ви захочете, щоб ваші блоки catch робили щось корисне. Є три найпоширеніші речі, які виконують блоки catch, коли вони зловили виняток:

   По-перше, блок catch може вивести повідомлення про помилку (або в консоль, або в лог-файл).

   По-друге, блок catch може повернути значення або код помилки назад в caller.

   По-третє, блок catch може згенерувати інший виняток. Оскільки блок catch не знаходиться всередині блоку try, то новий згенерований виняток оброблятиметься наступним блоком try.

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

Оцінити статтю:

1 Зірка2 Зірки3 Зірки4 Зірки5 Зірок (1 оцінок, середня: 5,00 з 5)
Loading...

Залишити відповідь

Ваш E-mail не буде опублікований. Обов'язкові поля відмічені *