20.x – Резюме к главе 20 и небольшой тест

Добавлено 13 сентября 2021 в 12:55

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

Для создания исключения используется инструкция throw. Блоки try ищут исключения, вызванные написанным или вызываемым в них кодом. Эти исключения направляются в блоки catch, которые перехватывают исключения определенных типов (при их совпадении) и обрабатывают их. По умолчанию перехваченное исключение считается обработанным.

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

Исключения могут быть любого типа данных, включая классы.

Блоки catch могут быть сконфигурированы для перехвата исключений определенного типа данных, или с помощью многоточия (...) обработчик catch может быть настроен для перехвата исключений всех типов данных. Блок catch, перехватывающий ссылку на базовый класс, также перехватывает исключения производного класса. Все исключения, создаваемые стандартной библиотекой, являются производными от класса std::exception (который находится в заголовке exception), поэтому перехват std::exception по ссылке перехватит все исключения стандартной библиотеки. Чтобы определить, какое именно исключение std::exception было сгенерировано, можно воспользоваться функцией-членом what().

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

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

Вы никогда не должны выбрасывать исключение в деструкторах.

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

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

Небольшой тест

Вопрос 1

Напишите класс дроби Fraction с конструктором, который принимает числитель и знаменатель. Если пользователь передает знаменатель 0, генерируется исключение типа std::runtime_error (включено в заголовок stdexcept). В функции main() попросите пользователя ввести два целых числа. Если дробь будет корректна, выведите ее. Если дробь некорректна, перехватите std::exception и сообщите пользователю, что он ввел недопустимую дробь.

Вот что должна вывести программа при выполнении:

Enter the numerator: 5
Enter the denominator: 0
Invalid denominator

#include <iostream>
#include <stdexcept> // для std::runtime_error

class Fraction
{
private:
    int m_numerator = 0;
    int m_denominator = 1;

public:
    Fraction(int numerator = 0, int denominator = 1) :
        m_numerator(numerator), m_denominator(denominator)
    {
        if (m_denominator == 0)
            throw std::runtime_error("Invalid denominator");
    }

    friend std::ostream& operator<<(std::ostream &out, const Fraction &f1);

};

std::ostream& operator<<(std::ostream &out, const Fraction &f1)
{
    out << f1.m_numerator << "/" << f1.m_denominator;
    return out;
}

int main()
{
    std::cout << "Enter the numerator: ";
    int numerator;
    std::cin >> numerator;

    std::cout << "Enter the denominator: ";
    int denominator;
    std::cin >> denominator;

    try
    {
        Fraction f(numerator, denominator);
        std::cout << "Your fraction is: " << f << '\n';
    }
    catch (std::exception& e)
    {
        std::cout << e.what() << '\n';
    }

    return 0;
}

Теги

C++ / CppException / ИсключениеLearnCppnoexceptstd::exceptionstd::runtime_errorГарантии безопасности исключенийДля начинающихОбработка ошибокОбучениеПрограммирование

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.