Кад дар даъвати истисно (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) може и да не бъдат извикани.