Урок №181. Исключения. Зачем они нужны?

  Юрий  | 

    | 

  Обновл. 27 Июн 2019  | 

 3069

Мы уже ранее говорили о механизмах обработки ошибок: 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 (36 оценок, среднее: 5,00 из 5)
Загрузка...

Добавить комментарий

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