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

  Юрий  | 

  |

  Обновл. 15 Сен 2021  | 

 28219

 ǀ   3 

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

Комментариев: 3

  1. Александр:

    все хорошо и понятно, но пример "мимо кассы"

    выражение:

    ни при каких обстоятельствах не приведет к сбою. Мало того, она выдает хорошо обрабатываемые результаты в случае b == 0:
    inf, при a != 0
    nan, при a == 0

    1. Саша:

      Проблема не в примере, он очень даже кстати. На ноль делить нельзя — это факт. Пример основывался на том, что на ноль делить нельзя и компилятор, из-за этого выдаёт ошибку(даже любой калькулятор выдаёт ошибку).

      1. boba:

        вот этим и отличается компилятор от калькулятора. как вуз от школы.
        в школе на ноль делить нельзя. а в вузе можно.
        и компилятор даже вполне себе ответ возвращает.

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

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