Обработка исключений в C++

Обработка исключений в C++На любом языке программирования, при разработке программы, в какой-то момент, мы гарантированно столкнемся с ошибкой.

Все ошибки можно разделить на два типа:

  • Ошибки времени компиляции: это ошибки, возникающие во время компиляции. Наиболее распространенными ошибками времени компиляции являются синтаксические ошибки, неправильные включения/импорты или неправильные ссылки.
  • Ошибки времени выполнения: эти ошибки возникают во время выполнения и называются исключениями.

Ошибки, возникающие во время выполнения, создают серьезные проблемы и, в большинстве случаев, мешают нормальному выполнению программы. Вот почему мы должны уметь обрабатывать эти ошибки. Эта обработка ошибок для поддержания нормального хода выполнения программы известна как «обработка исключений».

Обработка исключений

В C++ обработка исключений обеспечивается с помощью трех конструкций или ключевых слов: try, catch и throw.

Блок кода, обеспечивающий способ обработки исключения, называется «обработчиком исключений».

Общий синтаксис типичного обработчика исключений:

Код, который может генерировать исключения, заключен в блок try. Блок try также может содержать оператор «throw», который используется для явного генерирования исключений. Оператор «throw» состоит из ключевого слова «throw», за которым следует параметр, являющийся именем исключения. Затем этот параметр передается в блок catch.

Исключение, созданное в блоке «try», передается в блок «catch». Блок catch содержит код для обработки сгенерированного исключения. Он может содержать просто сообщение или целый блок кода для обработки исключения, чтобы не мешать нормальному ходу программы.

Необязательно, чтобы за каждым блоком try следовал только один блок catch. Если код нашего блока try содержит операторы, которые могут генерировать более одного исключения, то у нас может быть несколько блоков catch, в которых каждый блок catch обеспечивает обработку каждого из исключений.

В этом случае структура обработчика исключений выглядит так, как показано ниже:

Далее мы покажем исключения, поддерживаемые C++, вместе с их описанием.

Стандартные исключения C++

На следующей диаграмме показана иерархия классов исключений std::exception, поддерживаемых в C++:

Иерархия классов исключений в С++

В приведенной ниже таблице отображено описание каждого из приведенных выше исключений:

Исключение Описание
std::exception Это родительский класс для всех стандартных исключений C++.
std::bad_alloc Вызывается оператором ‘new’, когда выделение памяти идет неправильно.
std::bad_cast Возникает, когда ‘dynamic_cast’ идет не так.
std::bad_typeid Это исключение вызывается typeid.
std::bad_exception Это исключение используется для обработки непредвиденных исключений в программе.
std::runtime_error Исключения, возникающие во время выполнения и не определяемые простым чтением кода.
std::overflow_error Это исключение возникает, когда происходит математическое переполнение.
std::underflow_error Это исключение выдается, когда возникает математическая потеря значимости.
std::range_error Когда программа сохраняет значение, выходящее за пределы допустимого диапазона, возникает это исключение.
std::logic_error Эти исключения можно вывести, прочитав код.
std::out_of_range Исключение возникает, когда мы обращаемся/вставляем элементы, которые не находятся в диапазоне. Например, в случае векторов или массивов.
std::length_error Это исключение возникает в случае превышения длины переменной. Например, когда создается строка большой длины для типа std::string.
std::domain_error Генерируется при использовании математически недопустимого домена.
std::invalid_argument Исключение обычно возникает для недопустимых аргументов.

Читать также:  Типы наследования в C++

Использование try, catch и throw

Вы уже поняли, как операторы try, catch и throw используются при обработке исключений в C++. Теперь давайте посмотрим на пример программирования, чтобы лучше понять их работу.

Здесь мы представили классический случай ошибки времени выполнения «деление на ноль». Программа скомпилируется нормально, так как логических ошибок нет. Но во время выполнения, поскольку указанный знаменатель равен «0», программа обязательно выйдет из строя.

Чтобы предотвратить это, мы помещаем код разделения в блок try и обрабатываем исключение в блоке catch, чтобы программа выполнялась нормально.

Вывод данных:

Вышеприведенная программа является простой иллюстрацией «try throw catch» на C++. Как видим, в программе мы предоставляем значения числителя и знаменателя. Далее проверяем, равен ли знаменатель нулю. Если да, то выбрасываем исключение, иначе печатаем результат. В блоке catch мы печатаем сообщение об ошибке, вызванное исключением.

Это гарантирует, что программа не выйдет из строя или не завершится аварийно, когда она столкнется с делением на ноль.

Как остановить бесконечный цикл в обработке исключений

Рассмотрим еще один случай бесконечного цикла.

Если код внутри должен генерировать исключение, то нам нужно рассмотреть фактическое место, где мы должны поместить наш блок try-catch. То есть должен ли блок try-catch быть внутри цикла или цикл должен быть внутри блока try-catch.

Нет гарантированного ответа относительно размещения блока try-catch, однако это зависит исключительно от ситуации. Одно из соображений, которое мы должны учитывать, состоит в том, что блок try-catch обычно приводит к расширению программы. Поэтому, если это не требуется, мы должны отказаться от кода обработки исключений в бесконечном цикле.

Рассмотрим пример, показанный ниже:

Читать также:  Полиморфизм времени выполнения в C++

Здесь у нас есть бесконечный цикл while. Внутри цикла мы читаем входное число и добавляем его к сумме. Чтобы выйти из цикла, нам нужно указать условие завершения внутри цикла. Мы указали -99 в качестве конечного условия.

Как можете видеть, мы поместили этот код в блок try, и когда введенное число равно -99, мы генерируем исключение, которое перехватывается в блоке catch.

Теперь в приведенной выше ситуации, если мы поместим весь цикл while внутри блока try, тогда неизбежно возникнут неудобства, поскольку цикл while является бесконечным. Таким образом, лучшее место для размещения блока try-catch — внутри цикла.

Программа выдает следующий вывод данных:

Обратите внимание, что управление передается блоку catch после ввода -99 в качестве входных данных.

Обратите внимание, что мы не указали какой-либо конкретный объект исключения в качестве аргумента для перехвата. Вместо этого мы предоставили (…). Это указывает на то, что мы перехватываем обобщенное исключение.

Раскручивание стека

Вы уже знаете, что всякий раз, когда программа имеет модульную структуру и задействованы вызовы функций, текущее состояние программы сохраняется в стеке. Когда функция возвращается обратно, состояние восстанавливается и выполнение программы продолжается в обычном режиме.

Раскручивание стека обычно связано с обработкой исключений. Это процесс, в котором записи функций удаляются из стека вызовов во время выполнения. В случае C++ всякий раз, когда возникает исключение, стек вызовов, в котором хранятся записи функций, линейно ищется обработчиком исключения.

Все записи удаляются из стека до тех пор, пока не будет найдена запись функции с обработчиком исключения. Это означает, что раскручивание стека происходит, когда исключение не обрабатывается в той же функции, т.е. функция выдает исключение, но не предоставляет обработчик перехвата.

Давайте разберемся с процессом раскручивания стека на примере программирования:

Вывод данных:

В приведенной выше программе у нас есть четыре функции, которые вызываются друг из друга.

Путь вызова функции: main=>first_f()=>second_f()=> Third_f()=>last_f().

Следовательно, первые пять строк вывода говорят сами за себя.

В функции last_f выбрасывается исключение типа int. Но в этой функции нет блока catch и поэтому стек начинает раскручиваться. Таким образом, last_f завершается, и управление передается вызывающей функции, которая является Third_f. В этой функции также нет обработчика исключений, поэтому Third_f завершается и управление передается second_f.

Читать также:  Многомерные массивы в C++

В функции second_f есть обработчик исключения, но он не совпадает с исключением int и функция завершается.

Далее управление переходит к вызывающей функции first_f. В этой функции исключение int, сгенерированное функцией last_f, совпадает с предоставленным обработчиком исключений. Этот обработчик catch вызывается, и сообщение печатается. Затем печатается оператор, следующий за обработчиком catch, и управление возвращается к основной функции.

В main также есть обработчик исключения int, но поскольку он уже завершен, управление переходит к последнему оператору в основной функции, и печатается последний оператор.

Следовательно, в выводе программы вы можете видеть, что, поскольку не было обработчика исключения, который соответствовал бы исключению int, стек раскрутился до функции first_f. В результате функции, предшествующие функции first_f, были завершены.

Таким образом, благодаря раскрутке стека C++ дает нам преимущество в размещении обработчика исключений в подходящем месте. Таким образом, даже если функция просто выдает исключение и не хочет его обрабатывать, исключение будет распространяться до тех пор, пока не найдет подходящий обработчик исключения.

Исключение вне диапазона

Исключение « out_of_range » является наиболее распространенным исключением, с которым сталкивается программист. Следовательно, он нуждается в особом упоминании в этой статье.

Иногда, когда мы используем контейнеры, такие как массивы или векторы, мы определяем их размер или количество элементов, которые они содержат, во время компиляции. Поэтому, когда программист пытается вставить или получить доступ к элементам за пределами этого размера, возникает исключение «out_of_range».

Рассмотрим следующий пример программирования, чтобы это понять:

Вывод данных:

В приведенной выше программе у нас есть векторный контейнер размером 10. Затем мы пытаемся вставить элемент 11 в 11 -е место в векторе. Поскольку размер вектора объявлен равным 10, возникает исключение, выходящее за пределы диапазона, которое перехватывается в блоке catch программы, и точная причина, заданная функцией «what», выводится вместе с сообщением.

Итог

В этой статье мы рассмотрели обработку исключений в C++. Вы должны знать, что обработка исключений является неотъемлемой частью программы. Обработка исключений обеспечивает нормальное выполнение программы, чтобы при возникновении ошибки или исключительной ситуации программа не выходила из строя.

Таким образом, при использовании исключений нужно уметь использовать их с умом, чтобы сделать код надежным и эффективным. В следующей статье мы поговорим об аргументах командной строки в C++.

С Уважением, МониторБанк

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