Il sistema di eccezioni in C++ si basa sul meccanismo try-catch e sulla parola chiave throw. Quando si verifica una situazione eccezionale (ad esempio, un errore di risorsa, violazione di invarianti), all'interno di un blocco try viene generata (lanciata) un'eccezione tramite l'operatore throw. Le operazioni per la ricerca del gestore appropriato (catch) avvengono mostrando lo stack fino al primo tipo corrispondente.
Dettagli importanti:
std::exception).noexcept.Progettazione:
catch(const std::exception& e).Esempio di codice:
#include <iostream> #include <stdexcept> void mayFail(bool fail) { if (fail) throw std::runtime_error("Errore di processo"); } int main() { try { mayFail(true); } catch (const std::exception& ex) { std::cout << "Eccezione catturata: " << ex.what() << std::endl; } }
Cosa succede se un'eccezione viene lanciata da un distruttore durante la propagazione di un'altra eccezione nello stack?
Risposta: Avverrà un arresto anomalo del programma tramite la chiamata a std::terminate(), poiché durante la gestione di un'eccezione non è permesso generare una nuova eccezione (double exception), altrimenti si compromette lo stack unwinding.
Esempio:
struct CrashOnDestruct { ~CrashOnDestruct() noexcept(false) { throw std::runtime_error("Errore nel distruttore!"); } }; void func() { CrashOnDestruct obj; throw std::logic_error("Gestione errore"); } int main() { try { func(); } catch (...) { } } // Si concluderà tramite std::terminate()
Storia
All'interno di una grande applicazione finanziaria, le eccezioni venivano lanciate da un distruttore di una classe wrapper per un database. Una delle transazioni centrali, quando si verificava un errore, generava un'eccezione, e durante lo sforzo di unwind veniva chiamato il distruttore, che generava anche un errore. Tutta l'applicazione si arrestava anomamente, perdendo il lavoro degli utenti. I programmatori hanno dovuto sostituire in fretta il throw nei distruttori con la registrazione nei log e una gestione corretta.
Storia
Un microservizio gestiva le eccezioni usando: catch(std::exception). Uno dei thread lanciava eccezioni di tipi utente (non ereditati da std::exception). Questi errori non venivano catturati, si verificava un'interruzione imprevista della connessione e una perdita di memoria.
Storia
L'assenza del modificatore noexcept nei metodi di movimentazione dei contenitori ha portato il standard library a utilizzare una copia lenta invece di un'operazione di move rapida; un sostanziale fallimento nelle prestazioni è emerso solo nei test di carico.