Урок №195. Функціональний try-блок

  Юрій  | 

  Оновл. 7 Жов 2021  | 

 197

На цьому уроці ми розглянемо, що таке функціональний try-блок в мові С++, як його використовувати і які є нюанси.

Функціональний try-блок

Функціоналу блоків try і catch цілком достатньо в більшості випадків, але є одна конкретна ситуація, в якій їх стандартного функціоналу не вистачає. Розглянемо наступний код:

У програмі, наведеній вище, дочірній клас B викликає конструктор батьківського класу A, який генерує виняток (при успішному виконанні умови). Оскільки об’єкт b створюється в блоці try функції main(), то, якщо A згенерує виняток, блок try функції main() зловить його і передасть обробнику catch (int).

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

Oops!

Але що, якщо нам потрібно обробляти винятки всередині класу B? Виклик конструктора батьківського класу A відбувається через список ініціалізації членів, перед виконанням тіла конструктора класу B. Тому використовувати стандартний блок try тут не вийде.

У цій ситуації ми повинні використовувати злегка модифікований блок try — функціональний try-блок. Функціональний try-блок використовується для розміщення обробника винятків навколо всього тіла функції, а не тільки навколо її певної частини (блоку коду).

Синтаксис трохи складний для опису, тому розглянемо все на прикладі:

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

Construction of A failed
Oops!

Розглянемо цю програму детально.

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

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

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

У програмі, наведеній вище, оскільки ми явно не генеруємо виняток всередині блоку catch, виняток повторно генерується і передається caller-у на рівень вище, тобто в функцію main(). Блок catch функції main() ловить і обробляє виняток і ми отримуємо Oops!.

Хоча функціональні try-блоки також можуть використовуватися і зі звичайними функціями, які не є методами класу, це не поширена практика, тому що випадків, де вони можуть бути корисні, дуже і дуже мало. Вони майже завжди використовуються тільки з конструкторами!

Не використовуйте функціональний try-блок для очищення пам’яті

Якщо операція створення об’єкту неуспішна, то деструктор класу не викликається. Отже, у вас може виникнути спокуса використати функціональний try-блок для очищення класом частково виділеної йому пам’яті. Однак, звернення до членів об’єкта, який не створився, вважається невизначеною поведінкою, тому що об’єкт «мертвий» ще до початку виконання блоку catch. Це означає, що ви не можете використовувати функціональний try-блок для виконання очищення пам’яті класу.

Висновки

Функціональні try-блоки корисні в основному для записування помилок в лог-файл перед їх передачею на рівень вище в стеку викликів або для зміни типу згенерованого винятку.

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

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

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

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