Назад к вопросам
Middle+
108
questionbank

Приведите примеры ситуаций, когда может произойти повторное исключение в контексте использования конструкции 'double exception' (двойное исключение).

Sobes Copilot

Получайте ответы в реальном времени

Ответ от нейросети

sobes.tech AI

Двойное исключение (double exception) в C++ возникает, когда при обработке исключения (внутри блока catch) возникает новое исключение до того, как обработка предыдущего завершена. Это приводит к вызову std::terminate.

Примеры ситуаций:

  1. Выброс нового исключения в блоке catch:

    #include <iostream>
    #include <stdexcept>
    
    void func() {
        throw std::runtime_error("Первое исключение");
    }
    
    int main() {
        try {
            func();
        } catch (const std::runtime_error& e) {
            std::cerr << "Поймано: " << e.what() << std::endl;
            throw std::logic_error("Второе исключение в catch"); // Возникновение нового исключения
        }
        return 0;
    }
    

    Здесь в блоке catch, который обрабатывает std::runtime_error, выбрасывается новое исключение std::logic_error.

  2. Исключение во время очистки ресурсов в деструкторе в контексте обработки другого исключения:

    #include <iostream>
    #include <stdexcept>
    #include <vector>
    
    struct Resource {
        ~Resource() noexcept(false) { // Деструктор может выбросить исключение
            std::cerr << "Деструктор Resource вызван" << std::endl;
            if (true) { // Условие, вызывающее исключение
                throw std::runtime_error("Исключение в деструкторе Resource");
            }
        }
    };
    
    void risky_func() {
        Resource r; // Создается объект, деструктор которого может выбросить
        throw std::runtime_error("Исключение из risky_func"); // Первое исключение
    }
    
    int main() {
        try {
            risky_func();
        } catch (const std::runtime_error& e) {
            std::cerr << "Поймано: " << e.what() << std::endl;
            // Здесь может произойти исключение в деструкторе 'r', пока мы обрабатываем первое исключение
        }
        return 0;
    }
    

    Когда risky_func выбрасывает исключение, происходит раскрутка стека. В процессе раскрутки будет вызван деструктор объекта r. Если этот деструктор сам выбросит исключение, пока происходит обработка первого исключения, возникнет двойное исключение.

  3. Исключение в обработчике исключения из-за внутренних ошибок или вызовов функций, выбрасывающих исключения:

    #include <iostream>
    #include <stdexcept>
    #include <vector>
    
    void process_error(const std::exception& e) {
        std::cerr << "Обработка ошибки: " << e.what() << std::endl;
        // Предположим, эта функция может выбросить исключение при определенных условиях
        if (e.what() && std::string(e.what()).length() > 20) {
             throw std::logic_error("Ошибка при обработке слишком длинного сообщения"); // Второе исключение
        }
    }
    
    int main() {
        try {
            throw std::runtime_error("Это довольно длинное сообщение об ошибке"); // Первое исключение
        } catch (const std::exception& e) {
            process_error(e); // Этот вызов может выбросить второе исключение
        }
        return 0;
    }
    

    В этом случае, функция process_error, вызванная в блоке catch для обработки первого исключения, сама инициирует второе исключение.

Во всех этих сценариях, поскольку второе исключение возникает до завершения обработки первого (или во время раскрутки стека из-за первого), стандарт C++ требует вызова std::terminate. std::terminate по умолчанию вызывает abort().