Wanneer worden destructors niet aangeroepen bij het oproepen van een uitzondering?
sobes.tech AI
Antwoord van AI
De destructors van lokale objecten met automatische levensduur worden niet aangeroepen als er een uitzondering wordt geworpen in de constructor en het object niet volledig is geconstrueerd.
Ook worden de destructors niet aangeroepen voor:
- Objecten met een globale of statische levensduur.
- Objecten in rauwe geheugen die niet worden beheerd door standaardmechanismen (bijvoorbeeld, toegewezen via
mallocen niet vrijgegeven). - Objecten waarnaar slimme pointers wijzen die weglekken.
- Objecten waarvan de destructors zijn gemarkeerd als
noexcept(false)en zelf een uitzondering werpen tijdens het stack unwinding-proces. In dat geval eindigt het programma meestal abnormaal (std::terminate).
Voorbeeld van een destructor die niet wordt aangeroepen in de constructor:
#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; // Deze destructor wordt aangeroepen als er een uitzondering wordt geworpen in de constructor van MyClass
// Resource res2; // Als hier een uitzondering wordt geworpen, wordt res2 niet geconstrueerd
MyClass() {
// res1 is al geconstrueerd
std::cout << "MyClass constructor" << std::endl;
throw std::runtime_error("Error in MyClass constructor");
// res2 = Resource(); // Dit object wordt niet geconstrueerd als er eerder een uitzondering wordt geworpen
}
~MyClass() {
std::cout << "MyClass destructor" << std::endl;
}
};
int main() {
try {
MyClass obj; // Hier wordt een uitzondering geworpen
} catch (const std::runtime_error& e) {
std::cerr << "Caught exception: " << e.what() << std::endl;
}
return 0;
}
In dit voorbeeld wordt de destructor van MyClass niet aangeroepen omdat de constructor niet succesvol is voltooid. Echter, de destructor van res1 (onderdeel van MyClass) wordt wel aangeroepen, omdat res1 succesvol is geconstrueerd voordat de uitzondering werd geworpen.
Voorbeeld van een niet aangeroepen destructor bij geneste uitzondering:
#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"); // Werpt een uitzondering
}
};
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 wordt aangeroepen voordat dit blok wordt uitgevoerd
return 0;
}
In dit geval, wanneer de uitzondering in foo wordt geworpen, begint de stack unwinding. De destructor van BadDestructor wordt aangeroepen. Omdat deze destructor een uitzondering werpt (noexcept(false) staat dit toe), ontstaat er een tweede uitzondering tijdens de verwerking van de huidige uitzondering. Volgens de C++-standaard wordt in deze situatie std::terminate aangeroepen en wordt het programma beëindigd. De destructors van andere objecten op de stack (indien aanwezig vóór foo) worden mogelijk niet aangeroepen.