Le C++ est un langage gérant les exceptions depuis la version de 1990.
Malheureusement, il manque une fonctionnalité très pratique que l'on trouve dans la plupart des langages de haut niveau, et qui est la clause finally.
Une première solution consiste à faire ceci :
void function() { try { /* Function content */ } catch(...) { /* Finally */ throw; } }
Différents problèmes résultent.
Tout d'abord, ça n'est pas réellement équivalent au finally dans le sens où ça ne sera exécuté que si une exception est lancée, ce qui ne sera pas le cas dans le cas d'un retour dans le bloc try.
En outre, avec gdb par exemple, si une exception est lancée et n'est pas captuée par la suite, il sera impossible de récupérer cet objet, car il retournera au throw de ce catch, et donc il sera impossible de savoir où se trouve l'objet en question. (enfin peut être que si, mais je n'ai pas trouvé)
Solution
La solution consiste à utiliser une variable locale. En effet, les variables locales sont détruites lors du départ d'une fonction, que ça soit par un retour ou par saut due à une exception.
void function() { class Finally { public: ~Finally() { /* Finally */ } }; Finally finally; /* Instructions */ }
L'inconvénient est que c'est relativement chiant à écrire, et pas très élégant. En outre on perd l'interaction avec les variables locales car la fonction dans la classe ne peut pas accéder.
En effet, j'obtiens sinon l'erreur suivante :
m_show.cpp: In destructor ‘Client::m_show()::Finally::~Finally()’: m_show.cpp:270: error: use of ‘auto’ variable from containing function m_show.cpp:262: error: ‘TWRWTable* Table’ declared here
Les "auto" variables sont tout simplement les variables locales. Le mot-clef auto est implicite lors de la déclaration. Le problème réside dans le fait que je ne peux accéder à une variable "auto" d'un bloc suppérieur.
Ma première solution qui me vient à l'esprit est de la passer en argument :
TWRWTable* Table = new TWRWTable(t); class Finally { TWRWTable* table; public: Finally(TWRWTable* t) : table(t) {} ~Finally() { delete table; } }; Finally finally(Table);
Solution encore moins élégante, mais qui a l'avantage de marcher.
Généralisation
On constate qu'en général on utilise les finally pour détruire des objets alloués sur le tas dynamiquement.
L'idée de faire une classe générique réutilisable vient donc naturellement à l'esprit.
template<typename T> class Deleter { T* p; Deleter(const Deleter<T>&); Deleter<T>& operator=(const Deleter<T>&); public: explicit Deleter(T* _p) : p(_p) {} ~Deleter() { delete p; } };
On peut maintenant écrire :
void function(int t) { TWRWTable* Table = new TWRWTable(t); Deleter<TWRWTable> d(Table); /* Instructions */ }
Ce qui simplifie grandement la vie. Et pour des finally plus complexes, on peut toujours réutiliser une classe localement définie.
P.S.: Voici un exemple de ce que la lib boost utilise[ra] (je sais pas si ça a été inclus depuis, je n'ai jamais voulu utiliser cette bibliothèque) : scope_exit.
try: raise Exception("oups") print "jamais execute" except: print "erreur" finally: print "toujours execute"Résultat avec python 2.5 :
erreur
toujours execute
http://arb.developpez.com/c++/raii/shared_ptr/