Урок №194. Повторна генерація винятків

  Юрій  | 

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

 141

Іноді ви можете зіткнутися з ситуацією, коли потрібно зловити виняток, але обробляти його в даний момент часу не потрібно (або немає можливості). Наприклад, ви можете записати помилку в лог-файл, а потім передати її назад в caller для виконання фактичної обробки.

Відкладена обробка винятку

Це завдання можна легко виконати за допомогою кодів повернення, наприклад:

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

Тепер розглянемо наступну функцію:

У разі успіху виконання цієї функції повертається цілочисельне значення.

Але що, якщо щось піде не так з getIntValue()? В такому випадку, функція getIntValue() згенерує цілочисельний виняток, який буде перехоплено блоком catch в getIntValueFromDatabase(), який запише помилку в лог-файл. Але як ми потім повідомимо caller-у функції getIntValueFromDatabase(), що щось пішло не так? На відміну від функції з першого прикладу, тут немає хорошого коду повернення, який ми могли б використати (оскільки функція getIntValueFromDatabase() повертає цілочисельне значення, то будь-яке цілочисельне значення в якості коду повернення є допустимим).

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

Одним з очевидних рішень є генерація нового винятку:

У прикладі, наведеному вище, програма ловить виняток типу int з getIntValue(), записує помилку в лог-файл, а потім генерує новий виняток зі значенням q типу char. Хоча генерація винятку в блоці catch може здатися дивною витівкою, це не заборонено. Пам’ятайте, що тільки винятки, згенеровані в блоці try, можуть бути перехоплені блоком catch. Це означає, що виняток, згенерований в блоці catch, не буде перехоплено блоком catch, в якому він знаходиться. Замість цього стек почне розкручуватися, і виняток буде передано caller-у, який знаходиться на рівні вище в стеку викликів.

Виняток, згенерований в блоці catch, може бути винятком будь-якого типу — він не обов’язково повинен бути того ж типу, що й виняток, який обробляє блок catch.

Неправильна повторна генерація винятків

Альтернативним рішенням є повторна генерація одного і того ж винятку:

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

Розглянемо, що відбудеться в наступному випадку:

Тут функція getIntValue() генерує об’єкт класу Child в якості винятку, але блок catch приймає по посиланню об’єкт класу Parent. Це нормально, тому що, як ми вже дізналися на уроці №170, посилання батьківського класу може використовуватися для вказівки на дочірній об’єкт. Однак в такому випадку копія exception є класу Parent, а не класу Child! Іншими словами, відбудеться обрізка об’єкта класу Child()!

Ми можемо це побачити в наступній програмі:

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

Caught Parent p, which is actually a Child
Caught Parent p, which is actually a Parent

Той факт, що другий рядок вказує на те, що Parent насправді є Parent, а не Child, доводить, що відбулася обрізка об’єкту Child.

Правильна повторна генерація винятків

На щастя, мова C++ надає спосіб повторної генерації одного і того ж винятку. Для цього потрібно просто використати ключове слово throw всередині блоку catch без вказівки будь-якого ідентифікатора. Наприклад:

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

Caught Parent p, which is actually a Child
Caught Parent p, which is actually a Child

Ключове слово throw в блоці catch, яке, як здається на перший погляд, не генерує що-небудь конкретне, насправді генерує точно такий же виняток, який було тільки що оброблено блоком catch. Ніякого копіювання винятку і, отже, обрізки об’єкта не виконується.

Таким чином, цей метод повторної генерації винятку є пріоритетнішим для використання.

Правило: При повторній генерації винятку використовуйте ключове слово throw без вказівки будь-якого ідентифікатора.

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

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

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

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