Storia della questione:
Il meccanismo di gestione delle eccezioni è stato introdotto in C++ per garantire una gestione degli errori più affidabile e strutturata, a differenza dei codici di ritorno. Nel corso degli anni, la sintassi si è ampliata, sono stati introdotti i qualificatori noexcept per controllare la cancellazione delle eccezioni dalle funzioni.
Problema:
Un'errata gestione delle eccezioni porta spesso a perdite di memoria, comportamenti indefiniti o crash delle applicazioni. Se non si considerano le eccezioni sollevate dai costruttori, distruttori o durante la gestione delle risorse, si verifica un grave problema con lo stato del programma.
Soluzione:
La sintassi di base è l'uso di try-catch. Le eccezioni possono essere di qualsiasi tipo, ma si consiglia di ereditare le eccezioni personalizzate da std::exception. La parola chiave noexcept (C++11+) indica che una funzione non deve sollevare eccezioni; il lancio di un'eccezione da una funzione noexcept causa std::terminate().
Esempio di codice:
#include <iostream> #include <stdexcept> void func() noexcept(false) { throw std::runtime_error("Errore!"); } int main() { try { func(); } catch(const std::exception& ex) { std::cout << ex.what(); } }
Caratteristiche chiave:
È lecito lanciare eccezioni nel distruttore?
No, lanciare un'eccezione da un distruttore durante lo svuotamento dello stack causa std::terminate(). Le eccezioni nel distruttore devono essere catturate all'interno dello stesso distruttore.
Cosa succede se si lancia un'eccezione da una funzione noexcept?
Viene chiamato std::terminate, il programma termina in modo anomalo. noexcept è un contratto rigoroso.
Si possono catturare eccezioni per valore? Perché è pericoloso?
Si possono catturare per valore, ma l'oggetto viene copiato (object slicing). È consigliato utilizzare const &.
Esempio di codice:
try { throw std::out_of_range("errore"); } catch (const std::exception& e) { /* ... */ }
Il codice lanciava eccezioni nei distruttori delle risorse; in caso di terminazione anomala, lo stack si svuotava in modo errato, causando perdite di risorse.
Vantaggi:
Svantaggi:
I metodi critici erano contrassegnati come noexcept, i distruttori gestivano tutte le eccezioni all'interno di sé. Si utilizzavano catch per riferimento, tutti gli errori venivano registrati.
Vantaggi:
Svantaggi: