Prenons un exemple. Imaginons une classe qui prend en charge l'ouverture d'un fichier, et la gestion de son buffer. L'ouverture du fichier est fainéante, l'écriture aussi, et la fonction close lance l'écriture du buffer en dernier ressort. Une exception io_error est lancée lorsqu'une erreur d'écriture est détectée :
// c++ -o throwing_close{,.cpp} --std=c++11#include <iostream>#include <string>namespacemy_ns{classio_error{}/* class io_error */;classfile{public:// Lazy openerstaticfileconstopen(std::stringconst);voidflush(){throwio_error();}voidclose(){flush();}~file()/* file is not declared nothrow, no warranty on exception safety */{flush();close();}// Append content. Open in append mode if file isn't open yetvoidappend(std::stringconst){}}/* class file */;fileconstfile::open(std::stringconst){filenew_file{};returnnew_file;}}// namespace my_nsintmain(){std::stringcontent{"Big content that's going to overflow the harddrive"};{autof=my_ns::file::open("throwing_close");f.append(content);try{f.flush();f.close();}catch(my_ns::io_errorconst&e){std::cout<<"Catched exception!\n";}}try{autof=my_ns::file::open("throwing_close");f.append(content);// Destructor called: an exception is thrown from dtor}catch(my_ns::io_errorconst&e){std::cout<<"That can't be catched\n";}}
Le résultat sur la ligne de commande :
mickael@laptop:~/tmp$ ./throwing_close
Catched exception!
terminate called after throwing an instance of 'my_ns::io_error'
Abandon
Alors, évidement, ici la classe est facile à repenser, si toutefois elle faisait quelque chose. Mais imagine une architecture plus complexe, où l'exception safety n'est jamais garantie, où aucune signature n'indique qu'une fonction peut lancer une exception, où la libération d'une ressource peut être complexe et entraîner moult lancement d'exception.
Oui, il faut libérer les ressources dès qu'on le peut, ou des comportement indéfinis sont garantis (ici on notera que l'implémentation tue directement le processus qui aura tenté de lancer une exception depuis un destructeur, il n'est pas possible d'empêcher sa destruction).
Donc non, même en utilisant un objet RAII, il n'y a pas de garantie à ce que la ressource soit libérée si une exception peut être levée lors de l'exécution d'un destructeur.
[^] # Re: finally
Posté par LupusMic (site web personnel, Mastodon) . En réponse à la dépêche Peuch-peuch Cinq Cinq, pour PHP 5.5. Évalué à 2. Dernière modification le 03 juillet 2013 à 12:00.
Prenons un exemple. Imaginons une classe qui prend en charge l'ouverture d'un fichier, et la gestion de son buffer. L'ouverture du fichier est fainéante, l'écriture aussi, et la fonction close lance l'écriture du buffer en dernier ressort. Une exception io_error est lancée lorsqu'une erreur d'écriture est détectée :
Le résultat sur la ligne de commande :
mickael@laptop:~/tmp$ ./throwing_close
Catched exception!
terminate called after throwing an instance of 'my_ns::io_error'
Abandon
Alors, évidement, ici la classe est facile à repenser, si toutefois elle faisait quelque chose. Mais imagine une architecture plus complexe, où l'exception safety n'est jamais garantie, où aucune signature n'indique qu'une fonction peut lancer une exception, où la libération d'une ressource peut être complexe et entraîner moult lancement d'exception.
Oui, il faut libérer les ressources dès qu'on le peut, ou des comportement indéfinis sont garantis (ici on notera que l'implémentation tue directement le processus qui aura tenté de lancer une exception depuis un destructeur, il n'est pas possible d'empêcher sa destruction).
Donc non, même en utilisant un objet RAII, il n'y a pas de garantie à ce que la ressource soit libérée si une exception peut être levée lors de l'exécution d'un destructeur.