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

Расскажи о вызовах исключений (throw exception) в конструкторах и десторакторах.

Sobes Copilot

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

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

sobes.tech AI

Вызов исключения в конструкторе может привести к утечке ресурсов, если часть объектов или ресурсов внутри конструктора была успешно выделена до момента выброса исключения, а соответствующий деструктор при этом не будет вызван. Для предотвращения утечек рекомендуется использовать RAII (Resource Acquisition Is Initialization), например с помощью умных указателей или классов-оберток, которые гарантируют освобождение ресурсов при выходе из области видимости.

Вызов исключения в деструкторе приводит к неопределенному поведению. Если деструктор вызван в результате другого исключения (stack unwinding), и при этом сам выбросит необработанное исключение, программа завершится вызовом std::terminate. Согласно стандарту C++, деструкторы должны быть noexcept.

#include <iostream>
#include <memory> // Для std::unique_ptr

struct Resource {
    Resource() { std::cout << "Resource acquired\n"; }
    ~Resource() { std::cout << "Resource released\n"; }
};

struct BadConstructor {
    Resource res; // Ресурс будет выделен до выброса исключения
    BadConstructor() {
        std::cout << "BadConstructor constructor\n";
        throw std::runtime_error("Error in constructor"); // Выбрасываем исключение
        // Деструктор Resource не будет вызван, если исключение выброшено до завершения конструктора
    }
    ~BadConstructor() noexcept { // Деструкторы должны быть noexcept
        std::cout << "BadConstructor destructor\n";
    }
};

struct GoodConstructor {
    std::unique_ptr<Resource> res; // Использование умного указателя для RAII
    GoodConstructor() : res(std::make_unique<Resource>()) {
        std::cout << "GoodConstructor constructor\n";
        // Теперь, если выбросить исключение, unique_ptr гарантирует вызов деструктора Resource при выходе из scope
        // throw std::runtime_error("Error in constructor");
    }
    ~GoodConstructor() noexcept {
        std::cout << "GoodConstructor destructor\n";
    }
};

struct BadDestructor {
    BadDestructor() { std::cout << "BadDestructor constructor\n"; }
    ~BadDestructor() {
        std::cout << "BadDestructor destructor\n";
        // Нельзя выбрасывать исключение из деструктора, особенно при unwind
        // throw std::runtime_error("Error in destructor");
    }
};

int main() {
    // Пример с конструктором (приведет к утечке Resource без RAII)
    try {
        // BadConstructor bc; // Раскомментировать для демонстрации утечки
    } catch (const std::exception& e) {
        std::cout << "Caught exception: " << e.what() << "\n";
    }

    // Пример с хорошим конструктором (использует RAII)
    try {
        // GoodConstructor gc; // Раскомментировать для демонстрации, утечки не будет
    } catch (const std::exception& e) {
         std::cout << "Caught exception: " << e.what() << "\n";
    }

    // Пример с деструктором (может привести к std::terminate)
    try {
        // BadDestructor bd; // Раскомментировать, если хотите увидеть потенциальный terminate
        // throw std::runtime_error("Another exception"); // Исключение, которое вызовет unwind
    } catch (const std::exception& e) {
         std::cout << "Caught exception: " << e.what() << "\n";
    }

    return 0;
}

Сводная таблица:

Место вызова исключения Потенциальные проблемы Рекомендуемое решение
Конструктор Утечка ресурсов Использовать RAII (умные указатели, обертки)
Деструктор Неопределённое поведение, std::terminate при unwind Деструкторы должны быть noexcept, избегать выброса исключений