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

Когда при вызове исключения (exception) не вызываются деструкторы?

Sobes Copilot

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

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

sobes.tech AI

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

Также деструкторы не вызываются для:

  • Объектов с глобальным или статическим временем жизни.
  • Объектов в сырой памяти, которые не управляются стандартными механизмами (например, выделены через malloc и не были освобождены).
  • Объектов, на которые указывают утекающие умные указатели.
  • Объектов, чьи деструкторы помечены как noexcept(false) и сами бросают исключение во время раскрутки стека (stack unwinding). В этом случае программа обычно аварийно завершается (std::terminate).

Пример невызова деструктора в конструкторе:

#include <iostream>
#include <stdexcept>

class Resource {
public:
    Resource() {
        std::cout << "Resource acquired" << std::endl;
    }
    ~Resource() {
        std::cout << "Resource released" << std::endl;
    }
};

class MyClass {
public:
    Resource res1; // Этот деструктор будет вызван при исключении в конструкторе MyClass
    // Resource res2; // Если бы здесь бросалось исключение, res2 не был бы сконструирован

    MyClass() {
        // res1 уже сконструирован
        std::cout << "MyClass constructor" << std::endl;
        throw std::runtime_error("Error in MyClass constructor");
        // res2 = Resource(); // Этот объект не будет сконструирован, если выше брошено исключение
    }
    ~MyClass() {
        std::cout << "MyClass destructor" << std::endl;
    }
};

int main() {
    try {
        MyClass obj; // Здесь происходит исключение
    } catch (const std::runtime_error& e) {
        std::cerr << "Caught exception: " << e.what() << std::endl;
    }
    return 0;
}

В этом примере деструктор MyClass не вызывается, поскольку конструктор MyClass не завершился успешно. Однако, деструктор res1 (подчлена MyClass) будет вызван, так как res1 был успешно сконструирован до того, как было брошено исключение.

Пример невызова деструктора при повторном исключении:

#include <iostream>
#include <stdexcept>

class BadDestructor {
public:
    BadDestructor() {
        std::cout << "BadDestructor constructor" << std::endl;
    }
    ~BadDestructor() noexcept(false) {
        std::cout << "BadDestructor destructor" << std::endl;
        throw std::runtime_error("Error in destructor"); // Бросает исключение во время раскрутки стека
    }
};

void foo() {
    BadDestructor obj;
    throw std::runtime_error("Exception in foo");
}

int main() {
    try {
        foo();
    } catch (const std::runtime_error& e) {
        std::cerr << "Caught exception: " << e.what() << std::endl;
    }
    //std::terminate будет вызван до того, как этот блок будет выполнен
    return 0;
}

В этом случае, когда исключение брошено в foo, начинается раскрутка стека. Вызывается деструктор BadDestructor. Поскольку деструктор BadDestructor бросает исключение (noexcept(false) позволяет это), происходит повторное исключение во время обработки текущего исключения. По стандарту C++, в такой ситуации вызывается std::terminate, и программа завершается. Деструкторы других объектов в стеке (если бы они были до foo) могут не быть вызваны.