Historique de la question :
C++ a été initialement conçu avec un accent sur la performance, donc la gestion des ressources (mémoire, fichiers, flux, sockets) se fait souvent manuellement. Lorsqu'une exception se produit, il est nécessaire de nettoyer les ressources acquises. Le stack unwinding (déroulement de la pile) est un mécanisme utilisé par C++ pour terminer correctement l'exécution des fonctions lors du lancement d'une exception.
Problème :
Lorsqu'une exception est lancée, le contrôle passe immédiatement aux blocs catch, et les fonctions intermédiaires sont "déroulées" : leurs destructeurs sont appelés, mais les appels explicites aux fonctions de libération peuvent être omis (par exemple, si des objets ne sont pas utilisés pour libérer automatiquement les ressources).
Solution :
En C++, la libération des ressources doit être confiée aux destructeurs, et non pas effectuée par des appels aux fonctions de libération manuellement. Le modèle RAII (Resource Acquisition Is Initialization) est un moyen explicite de rendre la libération de ressources automatique. Lors du déroulement de la pile, le destructeur sera appelé et la ressource sera libérée indépendamment du chemin de sortie de la fonction.
Exemple de code :
#include <fstream> #include <stdexcept> void readFile(const std::string& filename) { std::ifstream file(filename); // S'ouvrira et se fermera correctement même en cas d'exception if (!file.is_open()) { throw std::runtime_error("Le fichier ne peut pas être ouvert"); } // ... lire le fichier } // file se fermera même en cas d'exception
Caractéristiques clés :
Si le code contient delete ptr; dans le bloc catch, est-ce suffisant pour libérer la mémoire ?
Non, si une exception se produit entre l'allocation de mémoire et le bloc catch, la mémoire peut ne pas être libérée. Il est préférable d'utiliser std::unique_ptr ou d'écrire delete dans le destructeur.
Exemple de code :
void foo() { int* data = new int[10]; // ... throw std::runtime_error("échec"); delete[] data; // ne sera pas appelé en cas d'exception }
Le stack unwinding peut-il sauter l'appel du destructeur pour un objet alloué sur la pile ?
Non, tous les objets locaux (non détruits jusqu'au point de lancement de l'exception) seront détruits dans l'ordre inverse de leur création, les destructeurs seront garantis d'être appelés.
Peut-on utiliser goto ou longjmp pour sortir d'un bloc try et s'attendre à ce que les destructeurs soient appelés ?
Non. C++ garantit l'appel des destructeurs uniquement lors du déroulement de la pile dû à une exception, et non à cause d'une gestion incorrecte du flux (goto, setjmp/longjmp).
Un programmeur alloue de la mémoire avec new, traite des exceptions en libérant la mémoire dans le bloc catch, mais oublie d'autres chemins de sortie de la fonction.
Avantages :
Inconvénients :
On utilise std::unique_ptr et des classes RAII pour toutes les ressources, la libération ne dépend pas de try/catch.
Avantages :
Inconvénients :