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

  Юрий Ворон  | 

    | 

  Обновлено 24 Ноя 2018  | 

 476

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

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

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