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

В чем заключается особенность выброса исключений в конструкторах и деструкторах?

Sobes Copilot

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

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

sobes.tech AI

Выброс исключения в конструкторе:

  • Если исключение выбрасывается в конструкторе, деструктор объекта не вызывается.
  • Необходимо позаботиться об освобождении уже выделенных ресурсов до момента выброса или использовать идиому RAII (Resource Acquisition Is Initialization) для управления ресурсами.
  • Частично сконструированный объект находится в неопределенном состоянии.

Выброс исключения в деструкторе:

  • Выброс исключения из деструктора в процессе раскрутки стека (stack unwinding) из-за другого исключения приводит к вызову std::terminate, которое прерывает выполнение программы. Это очень плохо.
  • Деструкторы должны быть noexcept.
  • Если в деструкторе необходимо выполнить операцию, которая может выбросить исключение (например, закрытие файла), следует обработать это исключение внутри деструктора (например, записать в лог) вместо его дальнейшего распространения.
#include <iostream>
#include <fstream>

class ResourceHolder {
    std::ofstream file;
    bool opened = false;

public:
    ResourceHolder(const std::string& filename) {
        file.open(filename);
        if (!file.is_open()) {
            // Выброс исключения в конструкторе
            throw std::runtime_error("Failed to open file: " + filename);
        }
        opened = true;
        std::cout << "Resource acquired successfully." << std::endl;
    }

    ~ResourceHolder() noexcept { // Деструктор noexcept
        if (opened) {
            try {
                file.close();
                std::cout << "Resource released successfully." << std::endl;
            } catch (const std::exception& e) {
                // Обработка исключения внутри деструктора
                std::cerr << "Exception caught in destructor: " << e.what() << std::endl;
                // НЕ выбрасываем исключение дальше
            }
        } else {
             std::cout << "Resource release skipped (not opened)." << std::endl;
        }
    }
};

int main() {
    try {
        // Пример выброса в конструкторе
        // ResourceHolder res("non_existent_file.txt");

        // Пример успешного конструирования и освобождения
        ResourceHolder res("test.txt");

    } catch (const std::runtime_error& e) {
        std::cerr << "Caught exception: " << e.what() << std::endl;
    }

    // Пример некорректного деструктора (для иллюстрации, НЕ ПОВТОРЯЙТЕ)
    /*
    class BadDestructor {
        int* data;
    public:
        BadDestructor() : data(new int(10)) {}
        ~BadDestructor() {
            delete data;
            // Симулируем выброс исключения в деструкторе (НЕ ДЕЛАЙТЕ ТАК)
            // throw std::runtime_error("Exception from bad destructor");
        }
    };

    try {
        BadDestructor bad_obj;
        throw std::runtime_error("Another exception"); // Вызываем другое исключение
    } catch (...) {
        // При наличии исключения в деструкторе при раскрутке стека
        // будет вызван std::terminate
         std::cerr << "Caught some exception in main." << std::endl;
    }
    */

    return 0;
}
Особенность Конструктор Деструктор
Вызов деструктора Не вызывается, если выброшено исключение Обычно вызывается (при успешном конструировании)
Неопределенное состояние Частично сконструированный объект -
RAII Важно для управления ресурсами -
noexcept Необязательно, но полезно знать о возможности ошибки Крайне рекомендовано быть noexcept
Раскрутка стека Участвует в процессе Выброс исключения во время раскрутки стека ведет к std::terminate