Pokud z destruktoru vyletí výjimka během stack unwindingu, tak se nezahodí, ale volá se std::terminate. Což obvykle znamená, že se destruktor ukončí i se zbytkem programu. Pro terminate se dá nastavit vlastní handler, ale v něm se stejně moc zachránit nedá.
No tohle je přesně ta krásná vlastnost C++, kterou naprosto nechápu... respektivě chápu, že lidi z ISO nebyli schopni najít rozumné řešní, tak se k tomu chovají jako pštros, schovají hlavu do písku a řeší to tím nejhorším způsobem, jakým se to řešit dá.
Základní pravidla dobrého programátora: "Program by neměl spadnout". Nelze zákazníkovy vysvětlovat, že program spadnul jen proto, že mu selhala nějaká operace a zrovna zároveň mu spadla síť, takže došlo k dvou problémům naráz a program to psychicky neunesl a zhroutil se
http://www.gotw.ca/gotw/047.htm
Nesmíte věřit všemu, co se na internetu píše. Ten člověk má sice pravdu, že ta funkce uncaught_exception je hloupá a vrací true, když probíhá stack unwinding, ale už nevyřeší, zda výjimka vyhozena v tomto okamžiku způsobí problém.
Jenže už to neumí vyřešit a navíc ukazuje příklady chybného použití této funkce. Nejprve ale jedna malá analýza.
V případě, že nechci použít uncaught_exception, tak ja budu výjimku v destruktoru řešit? No asi nijak, tak jak to řeši většina běžných coderů
~Object() {
try {
....
} catch (...) {}
}
Když budu chtít být podctivější, napíšu to takto
~Object() {
try {
....
} catch (std::exception &e) {
logException(e);
}
}
... tedy za předpokladu, že mi funkce logException nevyhodí výjimku. Benefit, který mi přináší použití std::uncaught_exception je ten, že mi říká naopak... "nyní je bezpečné výjimku vyhodit, takže se ten problém dostane k někomu povolanějšímu.". Samozřejmě, že není blbej nápad to zalogovat tak jako tak
~Object() {
try {
....
} catch (std::exception &e) {
logException(e);
if (!std::uncaught_exception()) throw;
}
}
To se mi tedy nejhůře může stát je, že se destruktor bude chcovat tak, jako by se choval v okamžiku, kdy bych ho deklaroval nothrow() a výjimku si pořešil uvnitř sám. Kde v tom vidíte problém?
Zpět k tomu odkazu. Ten člověk nakonec demonstruje, že nerozumí nástroji, který se mu dostal do ruky. Například,
// Variant: Another wrong solution
//
Transaction::~Transaction() {
if( uncaught_exception() ) {
RollBack();
}
}
Má pravdu, je to wrong solution a je to také ukázka nesprávné použití uncaught_exception(). On totiž nepoužívá tu funkci proto, aby se vyhnul dvojitý výjimce, kterou zrovna řeší, on to používá proto, že má obavu, že by Rollback() vyhodil výjimku a způsobil problém. A to je špatné použití uncaught_exception. Tak se to opravdu dělat nemá a ten kdo to použije si skutečně koleduje o malér. Správná řešení je:
Transaction::~Transaction() {
try {
RollBack();
} catch (...) {
if (!std::uncaught_exception()) throw;
}
}
No je to pořád stejné, pořád to je jen o tom, že se destruktor snaží rozhodnout, zda je bezpečné hodit výjimku nebo ne. To co autor navrhuje v tom článku nakonec je opravdu z říše pohádek... použití zavíracích funkcí totálně zničíme koncept RAII. Pokud už bych v tom viděl výhodu, tak maximálně v situaci, kdy zavírám potencionálně nebezpečnou operaci v destruktoru, a chci mít jistotu, že výjimka propadne až ke mě. Ta možnost ručního uzavření by tam mohla samozřejmě být, stejně tak jako můj objekt pro transakci obsahuje i funkci rollback() a její implicitní volání řeší pouze případy, kdy je potřeba transakci rollbacknout během výjimky. Ale nelze to vzít jako konečné řešení a výjimky zakázat.
// The right solution
//
T::~T() /* throw() */ {
// ... code that won't throw ...
}
Pan autor má smysl pro humor. Kolikrát mu program spadnul před zákazníkem na SIGABORT?
// Alternative right solution
//
T::Close() {
// ... code that could throw ...
}
T::~T() /* throw() */ {
try {
Close();
} catch( ... ) {
}
}
Tady ukazuje, že pan autor vlastně neudělal nic. Vyjímku zaignoruje (ani ji nezaloguje) a program sice nespadne, ale rozhodně nemusí fungovat. Pokud by do větve catch přidal
if (!std::uncaught_exception()) throw;
Rozhodně by svému kódu neublížil.
Opravdu to nejde udělat jinak?
Jde, ale musí se to umět.
[…] The deallocation function is called regardless of whether the destructor for the object or some element of the array throws an exception.
Dík, to jsem nevěděl, domníval jsem se, že u přetížených operátorů to bude problém... v "některých" překladačích.
Já používám
~Object()
try {
⋮
} catch (...) {
if (std::uncaught_exception())
return;
}
To jsem používal, ale mám pocit, že s tím má někdo problém... Mám pocit, že ve Visual Studiu se to nechovalo úplně správně. A zápis občas rozhodil nějaká IDE (myslím že eclipse).