pré-requis :
- connaître le mécanisme des exceptions
- savoir comment créer ses propres exceptions
- savoir attraper sainement une exception
Je crois que j'en avais déjà parlé, mais je le formalise: pourquoi ne faut-il jamais, je dis bien jamais, utiliser std::exit (et les autres fonctions de sa famille). Une première bonne raison à mes yeux: elle vient du C, donc par définition, on va avoir des problèmes avec. Souvent quand on conçoit un programme, le traitement d'erreur implique des erreurs fatales (arrêt du programme), que l'on gère ça bien par exceptions, ou bien comme un cochon avec des codes d'erreurs ("Failure is not an error" d'ailleurs, si vous voulez une petite discussion sur les exceptions -> http://forum.hardware.fr/forum2.ph [...] 499&cat=10 , devenez adepte de la méthode EAFP fin de la ). Donc cette erreur fatale, vous la matérialisez par un appel à std::exit(). Malheureusement, c'est le coup de grâce. std::exit est une fonction bête qui va tuer votre programme d'un coup. Vous programmez bien avec une foultitude d'objets, bien, mais std::exit sans fiche bien, vos destructeurs ne seront __jamais__ appelés ... c'est vraiment un arrêt brutal, imaginez les conséquences si vous devez absolument relâcher certaines ressources (fichiers, verrous, connexions réseaux, matériel ...). l'horreur !
Heureusement la solution est très simple dans le cas où cette erreur est inextrippable (impossible de retourner dans le main pour faire un joli return), il vous suffit de balancer une exception et de l'attendre. J'insiste bien sur le point « attendre » car le gestionnaire d'exceptions inattendues par défaut fera un appel à un truc genre _abort dont l'effet est le même que std::exit.
Donc enserrer votre main dans un joli bloc try et placer 2 bloc catch: le premier pour std::exception, c'est à dire pour ramasser tout ce que vous lancer (intentionnellement ou pas) et un deuxième catch(...) pour rammasser le reste. Le mécanisme des exceptions assurant la destruction des objets, nous voilà hors de danger, tout est propre et sur, les erreurs sont maitrisées.
Notez une synthaxe un peu inhabituelle dans l'exemple: le bloc try fonctionnel, c'est à dire pour enserrer une fonction (ou bien un constructeur, c'est là une autre discussion). c'est juste un exemple, je ferais sans doute un article sur cette construction, sachez qu'elle existe.
c++ exit.cpp -> gestion avec std::exit
c++ -DHFR_EXCEPTION exit.cpp -> gestion avec exception
comptez bien les instances zombies
Code :
- #include <iostream>
- #ifdef HFR_EXCEPTION
- #include <stdexcept>
- #endif
- // attention à tous ce qui commence par un C
- #include <cstdlib>
- using namespace std;
- // une classe avec un compteur
- // pour savoir qui est qui
- class Foo
- {
- static int compteur;
- int id;
- public:
- Foo()
- : id(++compteur)
- {
- cout << "Foo() " << id << '\n';
- }
- Foo(const Foo & )
- : id(++compteur)
- {
- cout << "Foo(const Foo & ) " << id << '\n';
- }
- ~Foo()
- {
- cout << "~Foo() " << id << '\n';
- }
- };
- int Foo::compteur=0;
- // une fonction où se produit une erreur fatale
- void Aurevoir(Foo b)
- {
- Foo local(b);
- Foo();
- // soudain c'est le drame
- #ifndef HFR_EXCEPTION
- exit(0);
- #else
- throw logic_error("Boom !" );
- #endif
- }
- int main()
- try
- {
- Foo a;
- Foo(); // temporaire
- Aurevoir(a);
- }
- catch(const exception &ex)
- {
- cerr << ex.what() << endl;
- }
- catch(...)
- {
- cerr << "Unknown exception !" << endl;
- }
|
remarquez que cette méthode (enchasser le main avec 1 try / 2 catch) est la marque de bons logiciels.
Message édité par Taz le 21-10-2004 à 11:29:55