Sobes.tech
Middle+

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

sobes.tech AI

Отговор от 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");
        // Resource 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) може и да не бъдат извикани.