Урок №189. Винятки. Навіщо вони потрібні?

  Юрій  | 

  Оновл. 28 Вер 2021  | 

 204

Ми вже раніше говорили про механізми обробки помилок в мові С++, такі як cerr(), exit() і assert(). Однак ми не встигли поговорити про ще одну дуже важливу тему — “Винятки в мові С++”. Зараз ми це виправимо.

Коли коди повернення не справляються

При написанні повторно використовуваного коду виникає необхідність в обробці помилок. Одним з найбільш поширених способів обробки потенційних помилок є використання кодів повернення (або «кодів завершення»), які повертає оператор return. Наприклад:

Ця функція повертає індекс першого символу переданого рядка, який збігається зі значенням змінної ch. Якщо символ не знайдено, то функція повертає -1 в якості індикатора помилки.

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

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

По-друге, функції можуть повертати лише одне значення. А що, якщо нам потрібно буде повернути як результат виконання функції, так і код завершення? Наприклад:

Тут потрібен механізм обробки помилок, тому що, якщо користувач передасть 0 в якості параметра b, відбудеться збій. Крім того, функція також повинна повернути і результат виконання операції static_cast<double>(a)/b. Як же це зробити? Один з варіантів — повернення результату операції або коду завершення по посиланню, наприклад:

По-третє, коли коду багато, то багато речей можуть піти не по плану, тому коди повернення потрібно постійно перевіряти. Розглянемо наступний фрагмент програми, в якому проводиться аналіз текстового файлу на наявність певних значень:

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

По-четверте, коди повернення не дуже добре поєднуються з конструкторами. Що станеться, якщо ми створимо об’єкт, а всередині конструктора відбудеться щось катастрофічне? Конструктори не можуть використовувати оператор return для повернення індикатора стану, а передача по посиланню може заподіяти масу незручностей, і її потрібно явно перевіряти. Крім того, навіть якщо ми це зробимо, об’єкт все одно створиться, і “лікувати” ми вже будемо наслідки (або обробляти, або видаляти).

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

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

Винятки

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

На наступному уроці ми розглянемо принципи обробки винятків в мові C++.

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

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

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

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