Skip to content

CPP 09. Обработка исключительных ситуаций в СPP. Решение проблем структурного программирования. Блоки try и catch. Блоки try и catch методов и конструкторов. Безопасный код относительно исключений. Обертывание исключения в exception_ptr. Задачи которые может решать исключение. Проблемы с динамической памятью при обработке исключительных ситуаций.

Dmitriy Pisarenko edited this page Jun 15, 2023 · 1 revision

Обработка исключительных ситуаций в С++.

Главный недостаток структурного программирования – если на каком-то нижнем уровне возникает ошибка, её необходимо протащить наверх, где её обработают спустя N уровней абстракции. Обработка ошибки совмещена вместе с логикой программы.

Идея - ПРОКИДЫВАТЬ ошибку СРАЗУ туда, где её можно обработать.

  • Пример

    try
    {
     // код, который может упасть
     // где-то произошёл throw <объект>
    }
    catch (<тип1>& <объект>) // если объект приводится к тип1, то обработает этот обработчик
    {
    }
    catch (<тип2>& <объект>) // если предыдущие обработчики не могут обработать,
    {                        // то передаётся исключение след. обработчику
    }
    catch (...) // перехватит любую исключительную ситуацию (порядок обработчиков важен)
    {}

Если ошибка никем не перехватилась, то программа падает.

Задачи обработчика:

  1. Выдать сообщение (пользователю или в лог)
    I. Информация об ошибке (в лог файл пишем)
    II. Время, когда она произошла
    III. Где произошла
    IV. Данные, которые привели к этой ошибке
  2. Обработать ситуацию (по возможности)
  3. Если ошибка критическая - корректно завершить программу (например, корректно закрыть связь с бд)

Плюсы:

  • Не протаскиваем ошибку
  • Всю обработку исключительной ситуации вынесли в одно место (отделили от логики задачи)
  • Можно легко развивать ПО и модифицировать

Проблемы с динамической памятью при обработке исключительных ситуаций:

  • Страшный код:
try
{
    A *pobj = new A;
    pobj->f();
    delete pobj;
}
catch(...)
{}

в методе f была выброшена ошибка.

Что происходит:

  1. Возникает искл ситуация
  2. Удаляются все временные объекты, которые создавались в блоке try
  3. Выходим на обработчик

Возникает утечка дин памяти.

Бросать исключение из деструкторов не стоит. Непредсказуемый результат. Если же в нём возникает исключение, то в нём мы должны её обработать.

Решение проблемы с помощью noexcept или throw():

Можно запретить методу обрабатывать исключительную ситуацию.

Два варианта решения проблемы:

void A::f() noexcept; // Первый способ
void A::f() throw();  // Второй способ
  • С noexcept при возникновении исключительной ситуации вызывается функция terminate(). Функция terminate() приводит к тому, что будут вызываться все деструкторы только временных объектов в порядке, обратном их созданию.
  • С throw() результат непредсказуем, это старый синтаксис, который лучше не использовать.

noexcept без параметров аналогичен noexcept(True) - это говорит о том, что данный метод не должен обрабатывать исключительную ситуацию. Если пишем noexcept(False) или throw(...), то этот метод может обрабатывать все исключительные ситуации, как и в случае если ничего не пишем

Определение исключительных ситуаций, которые метод может обрабатывать

Можно указать, какую исключительную ситуацию метод может обрабатывать.

  • тык

    void A::f() throw(<тип>)

Этот метод может обрабатывать все исключительные ситуации с этим типом и производными от него.

Ловля ошибки в разделе инициализации.

Array::Array(int q) try: mas(new double[q]), cnt(q)
{}
catch(const std::bad_alloc& exc)
{ cout<<exc.what()<<endl; }

Идея exception

У нас есть базовый класс std::exception. Этот базовый класс нам представляет виртуальный метод what(), возвращающий строку message. Стандартные ошибки являются производными от этого класса. Производные классы могут подменять метод what().

Например, есть стандартная ошибка bad_alloc - ошибка, связанная с выделением памяти.

И да, мы свои классы можем тоже порождать от этого базового класса! Пусть у нас есть класс Array, мы для него хотим создать объекты, которые отвечают за определенные ситуации. Назовём класс ErrorArray. От него уже будем порождать конкретные ошибки: некорректный индекс - ErrorIndex, ErrorAlloc (перехватываем bad_alloc на себя).

Любую ошибку с нашим классом мы можем перехватить. На уровне класса exp мы можем перехватить любую ошибку, связанную с нашим массивом.

Для нашего ПО таких уровней перехвата ошибок может быть много. Удобно модифицировать. Модифицируя наш класс, мы добавим новую ошибку, но она будет перехватываться на уровне базового класса, отвечающего за ошибки, связанные с объектом нашего класса.

Обертывание исключения в exception_ptr. Задачи которые может решать исключение

Обертывание исключения в exception_ptr - это механизм, который позволяет сохранить информацию об исключении, чтобы его можно было обработать в другом месте программы. exception_ptr это указатель на объект типа std::exception_ptr, который может хранить информацию об исключениях.

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

exception_ptr может быть получен из объекта std::exception_ptr, который возвращается из функции std::current_exception(). Эта функция вызывается в блоке catch для получения указателя на текущее исключение.

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

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

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

Clone this wiki locally