Lien vers le sujet : https://github.com/IUTInfoAix-M2103/TP4
Archives d’Auteur: alain
M2103 – TP5-Corrigés
Fichier Alarme.h
/** * * \file Alarme.h * * \authors S. Nabitz * * \date 04/04/2018 * * \version V1.0 * * \brief déclaration de la classe Alarme * **/ #ifndef _ALARME #define _ALARME namespace nsDigicode { class Alarme { public: void declencher (void); }; // Alarme } /* nsDigicode */ #endif /* _ALARME */
Fichier Alarme.cpp
/** * * \file Alarme.cpp * * \authors S. Nabitz * * \date 04/04/2018 * * \version V1.0 * * \brief définition de la classe Alarme * **/ #include <iostream> #include <unistd.h> // sleep() using namespace std; #include "Alarme.h" void nsDigicode::Alarme::declencher (void) { while (true) { cout << "Alarme : la porte n'est pas fermée" << endl; sleep(1); } } // declencher()
Fichier Archivage.h
/** * * \file Archivage.h * * \authors S. Nabitz * * \date 04/04/2018 * * \version V1.0 * * \brief déclaration de la classe Archivage * **/ #ifndef _ARCHIVAGE #define _ARCHIVAGE namespace nsDigicode { class Archivage { public: void archiver (int); }; // Archivage } /* ns_Digicode */ #endif /* _ARCHIVAGE */
Fichier Archivage.cpp
/** * * \file Archivage.cpp * * \authors S. Nabitz * * \date 04/04/2018 * * \version V1.0 * * \brief définition de la classe Archivage * **/ #include <fstream> #include <ctime> // time_t, time ()
using namespace std; #include "Archivage.h" #define NOM_FIC "../DigicodeIUT/Archive.txt" #define MODE std::fstream::out | std::fstream::app void nsDigicode::Archivage::archiver (int code) { ofstream leFichier (NOM_FIC,MODE); time_t dateActuelle; time (&dateActuelle); leFichier << "Code : " << code << " Date : " << ctime (&dateActuelle) << endl; leFichier.close (); } //archiver()
Fichier BD.h
/** * * \file BD.h * * \authors S. Nabitz * * \date 04/04/2018 * * \version V1.0 * * \brief déclaration de la classe BD * **/ #ifndef _BD #define _BD namespace nsDigicode { class BD { public: bool verifier (int); }; // BD } /* ns_Digicode */ #endif /* _BD */
Fichier BD.cpp
/** * * \file BD.cpp * * \authors S. Nabitz * * \date 04/04/2018 * * \version V1.0 * * \brief définition de la classe BD * **/ #include <iostream> #include <fstream> using namespace std; #include "BD.h" #define NOM_FIC "../DigicodeIUT/Base.txt" bool nsDigicode::BD::verifier (int code) {
int lu = -1; cout << "Vérification du code" << endl; ifstream leFichier; leFichier.open (NOM_FIC); while (!leFichier.eof () && lu != code ) leFichier >> lu; leFichier.close (); if (lu != code) cout << "Code erroné" << endl; return lu == code; } //verifier()
Fichier Capteur.h
/** * * \file Capteur.h * * \authors S. Nabitz * * \date 04/04/2018 * * \version V1.0 * * \brief déclaration de la classe Capteur * **/ #ifndef _CAPTEUR #define _CAPTEUR namespace nsDigicode { class Capteur { public: int detecter (void); }; // Capteur } /* ns_Digicode */ #endif /* _CAPTEUR */
Fichier Capteur.cpp
/** * * \file Capteur.cpp * * \authors S. Nabitz * * \date 04/04/2018 * * \version V1.0 * * \brief définition de la classe Capteur * **/ #include "Capteur.h" #include "kbhit.h" int nsDigicode::Capteur::detecter (void) { return _kbhit(); }
Fichier Chrono.h
/** * * \file Chrono.h * * \authors S. Nabitz * * \date 04/04/2018 * * \version V1.0 * * \brief déclaration de la classe Chrono * **/ #ifndef _CHRONO #define _CHRONO namespace nsDigicode { class ObjetTempo; class Chrono { bool actif; static void* compter (void *); public: void demarrer (ObjetTempo*); void arreter (void); Chrono (void); }; // Chrono } /* ns_Digicode */ #endif /* _CHRONO */
Fichier Chrono.cpp
/** * * \file Chrono.cpp * * \authors S. Nabitz * * \date 04/04/2018 * * \version V1.0 * * \brief définition de la classe Chrono * **/ #include #include using namespace std; #include #include "ObjetTempo.h" #include "Chrono.h" void nsDigicode::Chrono::arreter (void) { actif = false; } nsDigicode::Chrono::Chrono (void) { actif = false; } void* nsDigicode::Chrono::compter (void * p) { double duree = (clock_t) ( (ObjetTempo *) p)->getDelai (); Chrono *lec = ( (ObjetTempo *) p)->getChrono (); clock_t debut = clock (); double tempsEcoule = (clock () - debut) / CLOCKS_PER_SEC; lec->actif = true; while (lec->actif && tempsEcoule<duree) {="" tempsecoule="(clock" ()="" -="" debut)="" clocks_per_sec;="" }="" if="" (tempsecoule="">= duree ) { ( (ObjetTempo *) p)->finTempo (); lec->actif = false; } pthread_exit (NULL); } //compter() void nsDigicode::Chrono::demarrer (ObjetTempo * a) { pthread_t thread; while (actif) std::cout.flush (); // double duree = a->getDelai (); std::cout.flush (); pthread_create (&thread, NULL, compter, (void *) a); } //demarrer() </duree)>
Fichier Clavier.h
/** * * \file Clavier.h * * \authors S. Nabitz * * \date 04/04/2018 * * \version V1.0 * * \brief déclaration de la classe Clavier * **/ #ifndef _CLAVIER #define _CLAVIER namespace nsDigicode { class Clavier { public: int saisieChiffre (void); }; // Clavier } /* ns_Digicode */ #endif /* _CLAVIER */
Fichier Clavier.cpp
/** * * \file Clavier.cpp * * \authors S. Nabitz * * \date 04/04/2018 * * \version V1.0 * * \brief définition de la classe Clavier * **/ #include #include "Clavier.h" #include "kbhit.h" using namespace std; int nsDigicode::Clavier::saisieChiffre (void) { int c ; if (c = _kbhit()) { cout << "*" << flush; return c - '0'; } return -1; } // saisieChiffre()
Fichier Interface.h
/** * * \file Interface.h * * \authors S. Nabitz * * \date 04/04/2018 * * \version V1.0 * * \brief déclaration de la classe Interface * **/ #ifndef _INTERFACE #define _INTERFACE #include #include "ObjetTempo.h" #include "Clavier.h" #include "Voyant.h" #include "Chrono.h" namespace nsDigicode { class Interface : public ObjetTempo { int nbChiffresAttendus; Clavier leClavier; Voyant leVoyantVert; Voyant leVoyantRouge; void statut (std::string = "\n"); public: int saisieCode (void); Interface (Chrono*, int, int); }; // Interface } /* ns_Digicode */ #endif /* _INTERFACE */
Fichier Interface.cpp
/** * * \file Interface.cpp * * \authors S. Nabitz * * \date 04/04/2018 * * \version V1.0 * * \brief définition de la classe Interface * **/ #include #include using namespace std; #include "Interface.h" #include "Chrono.h" nsDigicode::Interface::Interface (Chrono * lech, int d, int nbca) : ObjetTempo (lech, d), leVoyantRouge (rouge, eteint), leVoyantVert (vert, eteint), nbChiffresAttendus (nbca) {} void nsDigicode::Interface::statut (string s) { cout << endl << "VERT" << setw(4) << leVoyantVert.getStatut() << setw(7) << "ROUGE" << setw(4) << leVoyantRouge.getStatut() << setw(24) << s << flush; } // statut() int nsDigicode::Interface::saisieCode (void) { int code, chiffreLu, nbChiffresLus; attente = false; do { leVoyantVert.eteindre (); leVoyantRouge.allumer (); statut ("Entrez votre code : "); cin.clear (); code = 0; while ((code = leClavier.saisieChiffre ()) == -1); nbChiffresLus = 1; leVoyantVert.allumer (); statut ("Continuez la saisie : "); attente = true; leChrono->demarrer (this); while (attente && nbChiffresLus < nbchiffresattendus) {
while (attente && (chiffrelu = leClavier.saisieChiffre())
== -1);
if (attente) {
code = code * 10 + chiffrelu;
nbchiffreslus++;
if (nbchiffreslus == nbchiffresattendus)
lechrono->arreter ();
} else cout << endl << "Temps ecoulé - Fin de saisie" << endl; } while (nbChiffresLus < nbChiffresAttendus ); leVoyantRouge.eteindre (); statut (); return code; } // saisieCode() </nbchiffresattendus)>
Fichier kbhit.h
/** * * \file kbhit.h * * \authors S. Nabitz * * \date 04/04/2018 * * \version V1.0 * * \brief déclaration de la fonction _kbhit() * **/ int _kbhit (void);
Fichier kbhit.cpp
/** * * \file kbhit.cpp * * \authors S. Nabitz * * \date 04/04/2018 * * \version V1.0 * * \brief définition de la fonction _kbhit() * **/ #include #include #include #include #include using namespace std; void set_mode (bool want_key) { static struct termios old, cur; if (!want_key) { tcsetattr (STDIN_FILENO, TCSANOW, &old); return; } tcgetattr (STDIN_FILENO, &old); cur = old; cur.c_lflag &= ~(ICANON | ECHO); tcsetattr (STDIN_FILENO, TCSANOW, &cur); } int _kbhit (void) { int c = 0; struct timeval tv; set_mode (true); fd_set fs; tv.tv_usec = tv.tv_sec = 0; FD_ZERO (&fs); FD_SET (STDIN_FILENO, &fs); select (STDIN_FILENO + 1, &fs, 0, 0, &tv); if (FD_ISSET (STDIN_FILENO, &fs)) { c = cin.get (); set_mode (false); } return c; } // _kbhit()
Fichier ObjetTempo.h
/** * * \file ObjetTempo.h * * \authors S. Nabitz * * \date 04/04/2018 * * \version V1.0 * * \brief déclaration de la classe ObjetTempo * **/ #ifndef _OBJETTEMPO #define _OBJETTEMPO #include "Chrono.h" namespace nsDigicode { class ObjetTempo { int delai; protected: bool attente; Chrono* leChrono; public: Chrono* getChrono (void); void finTempo (void); ObjetTempo (Chrono*, int); int getDelai (void) const; }; // ObjetTempo } /* ns_Digicode */ #endif /* _OBJETTEMPO */
Fichier ObjetTempo.cpp
/** * * \file ObjetTempo.cpp * * \authors S. Nabitz * * \date 04/04/2018 * * \version V1.0 * * \brief définition de la classe ObjetTempo * **/ #include "ObjetTempo.h" #include "Chrono.h" using namespace std; nsDigicode::ObjetTempo::ObjetTempo (Chrono* lech, int d) : leChrono (lech), delai (d) {} void nsDigicode::ObjetTempo::finTempo (void) { attente = false; } int nsDigicode::ObjetTempo::getDelai (void) const { return delai; } nsDigicode::Chrono* nsDigicode::ObjetTempo::getChrono (void) { return leChrono; }
Fichier Porte.h
/** * * \file Porte.h * * \authors S. Nabitz * * \date 04/04/2018 * * \version V1.0 * * \brief déclaration de la classe Porte * **/ #ifndef _PORTE #define _PORTE #include "ObjetTempo.h" #include "Capteur.h" #include "Chrono.h" namespace nsDigicode { class Porte : public ObjetTempo { Capteur leCapteur; bool fermer(); public: bool ouvrir(); Porte(Chrono*, int); }; // Porte } /* ns_Digicode */ #endif /* _PORTE */
Fichier Porte.cpp
/** * * \file Porte.cpp * * \authors S. Nabitz * * \date 04/04/2018 * * \version V1.0 * * \brief définition de la classe Porte * **/ #include #include "Porte.h" using namespace std; nsDigicode::Porte::Porte (Chrono * lech, int d) : ObjetTempo(lech, d) {} bool nsDigicode::Porte::ouvrir (void) { attente = true; leChrono->demarrer(this); cout << "Vous pouvez entrer" << endl; while (attente && !leCapteur.detecter()); if (attente) return fermer (); else { cout << "Temps écoulé - La porte se verrouille" << endl; return true; } } // ouvrir() bool nsDigicode::Porte::fermer (void) { cout << "Porte ouverte : fermez la porte" << endl; while (attente && !leCapteur.detecter ()); if (attente) { leChrono->arreter (); attente = false; cout << "Vous avez fermé la porte" << endl; return true; } else return false; } // fermer()
Fichier Voyant.h
/** * * \file Voyant.h * * \authors S. Nabitz * * \date 04/04/2018 * * \version V1.0 * * \brief déclaration de la classe Voyant * **/ #ifndef _VOYANT #define _VOYANT #include namespace nsDigicode { enum Color {rouge, vert}; enum Etat {allume, eteint}; class Voyant { Color couleur; Etat et; public: void allumer (void); void eteindre (void); Voyant (Color c, Etat e); std::string getStatut (void) const; }; // Voyant } /* ns_Digicode */ #endif /* _VOYANT */
Fichier Voyant.cpp
/** * * \file Voyant.cpp * * \authors S. Nabitz * * \date 04/04/2018 * * \version V1.0 * * \brief définition de la classe Voyant * **/ #include "Voyant.h" nsDigicode::Voyant::Voyant(Color c, Etat e) { couleur = c; et = e; } // Voyant() void nsDigicode::Voyant::allumer (void) { if (et == eteint) et = allume; } // allumer() void nsDigicode::Voyant::eteindre (void) { if (et == allume) et = eteint; } // eteindre() string nsDigicode::Voyant::getStatut (void) const { if (et == allume) return "ON"; return "OFF"; } // getStatut()
Fichier Systeme.h
/** * * \file Systeme.h * * \authors S. Nabitz * * \date 04/04/2018 * * \version V1.0 * * \brief déclaration de la classe Systeme * **/ #ifndef _SYSTEME #define _SYSTEME #include "Interface.h" #include "Porte.h" #include "Alarme.h" #include "BD.h" #include "Archivage.h" #include "Chrono.h" namespace nsDigicode { class Systeme { Interface lInterface; Porte laPorte; Alarme lAlarme; BD laBD; Archivage lArchive; Chrono leChrono; public: Systeme (int, int, int); }; // Systeme } /* ns_Digicode */ #endif /* _SYSTEME */
Fichier Systeme.cpp
/** * * \file Systeme.cpp * * \authors S. Nabitz * * \date 04/04/2018 * * \version V1.0 * * \brief définition de la classe Systeme * **/ #include using namespace std; #include "Systeme.h" #define DELAI_CLAVIER 5 #define DELAI_PORTE 8 #define NB_CHIFFRES_CLAVIER 4 nsDigicode::Systeme::Systeme (int dc, int dp, int nbca) : lInterface (&leChrono, dc, nbca), laPorte (&leChrono, dp) { int c; while (true) { lArchive.archiver (c = lInterface.saisieCode()); if (laBD.verifier (c)) { if (!laPorte.ouvrir ()) lAlarme.declencher (); } } } // Systeme() int main (void) { nsDigicode::Systeme s (DELAI_CLAVIER, DELAI_PORTE, NB_CHIFFRES_CLAVIER); return 0; } // main()
M2103-TP4-Exo-1
Dès qu’elle atteint une certaine taille, il est important qu’une application soit décomposée en plusieurs niveaux hiérarchiques clairs, pour sa lisibilité, sa maintenabilité et son évolutivité.
Le plus haut niveau est la fonction main()
.
C’est par l’exécution de cette fonction que commence (presque …) l’application.
Il est possible de lui faire passer des informations (des arguments) au lancement de la commande correspondante : options, nombres, noms de fichiers, etc. :
CopyFile FicDest FicSource -b
Cette possibilité a déjà été étudiée.
Le rôle de la fonction main()
est donc de commencer par analyser et valider ces arguments.
Puis vient l’exécution d’une ou de plusieurs fonctions relativement générales (pour nous les fonctions testXxx()
).
Enfin, avant de “rendre la main” au système, la fonction main()
doit clore proprement l’application :
- ne pas laisser remonter une exception non capturée, qui provoquerait le désagréable et sibyllin message :
abort
sans autre explication,
- préparer le “compte-rendu” de l’exécution sous la forme d’un entier renvoyé à la procédure appelante (en général le shell) : la fameuse variable
$?
.
En d’autres termes, une application pourrait avoir la structure suivante :
... int main (...) { validerArguments (); initialisation (); phase_1 (); ... phase_N (); terminaison (); return compteRendu; } // main()
En principe, dans une application idéale, toutes les exceptions levées devraient être traitées localement, ou à un niveau hiérarchique supérieur, mais aucune ne devraient remonter jusqu’à la fonction main()
.
On peut cependant supposer :
- que certaines exceptions sont levées “à l’insu” du développeur, et non capturées par la suite (nul n’est parfait !)
- que certaines exceptions sont délibérément ignorées et non capturées car l’erreur est tellement grave qu’aucun traitement ne peut la corriger et que la seule chose à faire est de laisser se terminer l’application.
Chaque étape de l’application étant susceptible de lever une exception, la structure de la fonction main()
devrait être la suivante :
... int main (...) { try { validerArguments(); initialisation(); phase_1(); ... phase_N(); terminaison(); return compteRenduDeSucces ; // = 0 } catch (...a_preciser...) { affichageApproprie (); return compteRenduDEchec ; // O < n < 255 } } // main()
Tout objet ou toute valeur peut servir d’exception.
Cependant, pour suivre les recommandations de la norme de C++ et de tous les spécialistes, les seules exceptions à utiliser devraient être les exceptions standard ou de classes dérivées par l’utilisateur de la classe standard exception
, ce qui est le cas de notre classe CException
.
Nous nous limiterons donc à capturer trois sortes d’exceptions :
- les exceptions standard,
- nos exceptions
CException
, - toutes les autres.
En cas de capture d’une exception inconnue, nous utiliserons la constante KExcInconnue
(= 255
) comme compteRenduDEchec
.
En cas de capture d’une exception standard exception
, nous utiliserons la constante KExcStd
(= 254
) comme compteRenduDEchec
,
En cas de capture d’une exception CException
, nous utiliserons la donnée-membre myCodErr
comme compteRenduDEchec
.
En cas de déroulement normal, nous utiliserons les constantes KNoError
ou KNoExc
(= 0
) comme compteRenduDeSucces
.
Nous considèrerons que toutes les fonctions de niveau immédiatement inférieur à main()
(validerArguments()
, initialisation()
, phase_1()
, phase_N()
, terminaison()
dans l’exemple ci-dessus, testXxx()
dans les TPs habituels – comme nous l’avons toujours fait), sont susceptibles de lever n’importe quelle exception.
En conséquence, nous ne donnerons aucune indication d’exception dans leur profil :
void testXxx (void) { ...
et non
void testXxx (void) throw (n_importe_quoi) { ...
Travail à réaliser
Créez le projet ExceptionsInMain
.
Y copier le contenu des fichiers CException.h
,
/** * * \file CException.h * * \authors M. Laporte, D. Mathieu * * \date 10/02/2011 * * \version V1.0 * * \brief Declaration de la classe CException * **/ #ifndef __CEXCEPTION_H__ #define __CEXCEPTION_H__ #include <exception> #include <string> #include "CstCodErr.h" namespace nsUtil { class CException : public std::exception { std::string myLibelle; unsigned myCodErr; public : CException (const std::string & libelle = std::string(), const unsigned codErr = KNoExc) noexcept; virtual ~CException (void) noexcept; const std::string & getLibelle (void) const noexcept; unsigned getCodErr (void) const noexcept; virtual const char* what (void) const noexcept; void display (void) const; }; // CException } // namespace nsUtil #endif /* __CEXCEPTION_H__ */
CException.cpp
/** * * \file CException.cpp * * \authors M. Laporte, D. Mathieu * * \date 10/02/2011 * * \version V1.0 * * \brief classe CException * **/ #include <iostream> #include <string> #include "CstCodErr.h" #include "CException.h" using namespace std; #define CEXC nsUtil::CException //========================== // Classe nsUtil::CException //========================== CEXC::CException (const string & libelle /* = string () */, const unsigned codErr /* = KNoExc */) noexcept : myLibelle (libelle), myCodErr (codErr) {} const string & CEXC::getLibelle (void) const noexcept { return myLibelle; } // GetLibelle() unsigned CEXC::getCodErr (void) const noexcept { return myCodErr; } CEXC::~CException (void) noexcept {} const char* CEXC::what (void) const noexcept { return myLibelle.c_str(); } void CEXC::display (void) const { cout << "Exception : " << myLibelle << '\n' << "Code : " << myCodErr << endl; } // Afficher() #undef CEXC
et CstCodErr.h
/** * * \file CstCodErr.h * * \authors M. Laporte, D. Mathieu * * \date 10/02/2011 * * \version V1.0 * * \brief Codes d'erreurs * **/ #ifndef __CSTCODERR_H__ #define __CSTCODERR_H__ namespace nsUtil { enum {KNoExc = 0, KNoError = 0, KExcStd = 254, KExcInconnue = 255 }; } // namespace nsUtil #endif /* __CSTCODERR_H__ */
(toutes les constantes représentant des codes d’erreurs, quels qu’ils soient, dans toutes les applications futures qui vous seront proposées, seront ajoutées dans ce fichier qui sera donc inclus très fréquemment).
Dans l’espace de noms anonyme du fichier testExceptionsInMain.cpp
, écrire la fonction testExceptionsInMain()
qui lève (schéma de choix) :
- soit une exception de base, en appelant le constructeur
exception()
de la classeexception
, - soit une exception standard plus spécifique,
- soit directement en appelant par exemple le constructeur
runtime_error()
de la classeruntime_error
et en lui passant un paramètre effectif (un libellé), - soit indirectement en appelant par une fonction dont vous savez qu’elle lève un exception standard, par exemple la fonction
at()
de la classestring
avec un indice invalide, qui lève une exceptionout_of_range
,
- soit directement en appelant par exemple le constructeur
- soit une exception
CException
, - soit une exception quelconque (un entier par exemple).
Dans la fonction main()
, effectuer les modifications correspondantes aux indications ci-dessus en capturant dans l’ordre :
- les exceptions
CException
, - les exceptions spécifiques standard (
runtime_error
ou autre), - les exceptions standard (
exception
), - toutes les autres exceptions.
Compilez et testez.
Variante 1
Dans la fonction main()
, permutez la capture des exceptions CException
et des exceptions runtime_error
et testez à nouveau.
Vous devez ne constater aucun changement.
Variante 2
Avant la capture de toute exception (catch (...)
), ajoutez la capture d’un unsigned
et levez une exception entière.
Vous constatez qu’elle n’est pas capturée : il n’y a aucun transtypage/conversion entre les exceptions levées et les exceptions capturées.
Remplacez la capture d’un unsigned
par celle d’un int
et répétez le test.
L’exception est correctement capturée.
Variante 3
Dans la fonction main()
, permutez la capture des exceptions exception
et des exceptions runtime_error
et testez à nouveau.
Vous devez constater que les exceptions runtime_error
ne sont plus jamais capturées (ce qui vous est d’ailleurs indiqué par un message warning
lors de la compilation).
Ceci est dû au fait qu’une exception runtime_error
est d’une classe dérivée de exception
, donc plus spécifique, et qu’une instruction catch
qui capture les exceptions exception
capturent aussi les exceptions dérivées.
Vous remarquez cependant que, bien que capturant apparemment une exception
, le programme affiche cependant le
string
que vous avez passé au constructeur de runtime_error
.
C’est du polymorphisme.
En conséquence, vous ne garderez à l’avenir que les captures des deux exceptions CException
et exception
, et toutes les autres.
Variante 4
Au lieu de lever directement une exception runtime_error
, provoquez une exception par une fonction standard : utilisez la fonction at()
membre de la classe string
avec une valeur d’indice invalide.
Compilez et testez
Ne pas oublier de sauvegarder les fichiers sources du projet sur github
.
M2103-TP4-Exo-1-Corrigé
namespace { void clearScreen (void) { cout << "\033[2J\033[1;1H" << flush; } // clearScreen() char choixDansMenu (void) { clearScreen(); cout << "A : exception 'exception'\n" "B : exception standard specifique\n" "C : exception 'CException'\n" "D : exception inconnue\n\n" "Votre choix (suivi de) : "; char choix; cin >> choix; clearScreen(); return choix; } // ChoixDansMenu() void traiterCommande (char cmd) { switch (cmd) { case 'A' : case 'a' : throw exception (); case 'B' : case 'b' : { // throw runtime_error ("erreur d'execution ..."); string s; cout << s.at (0); break; // inutile puisqu'une exception est levee avant } case 'C' : case 'c' : throw CException ("Surprise, surprise !", 123); case 'D' : case 'd' : throw 123; } } // traiterCommande() void testExceptionsInMain () { for ( ; ; ) traiterCommande (choixDansMenu()); } // testExceptionsInMain() } // namespace int main () { try { testExceptionsInMain (); return KNoExc; } catch (const CException & e) { cerr << "Erreur : " << e.getLibelle () << '\n' << "Code d'erreur = " << e.getCodErr () << '\n'; return e.getCodErr(); } catch (const out_of_range & e) // levee par string::at() { cerr << "Exception out_of_range : " << e.what () << '\n'; return KExcStd; } */ /* catch (const runtime_error & e) { cerr << "Exception runtime_error : " << e.what () << '\n'; return KExcStd; } */ catch (const exception & e) { cerr << "Exception standard : " << e.what () << '\n'; return KExcStd; } /* catch (const unsigned & e) { cerr << "Exception unsigned : " << e << '\n'; return KExcStd; } catch (const int & e) { cerr << "Exception int : " << e << '\n'; return KExcStd; } */ catch (...) { cerr << "Exception inconnue\n"; return KExcInconnue; } } // main()
M2103-TP4-Exo-2
Jusqu’à présent, nous avons vu qu’il est toujours possible de consulter l’état d’un flux pour tester la fin de fichier ou vérifier a posteriori la réussite ou l’échec d’une opération, d’extraction par exemple.
Dans tous les cas, cela demande au développeur une démarche active : c’est à lui de vérifier à chaque opération l’état du flux.
Le C++ offre une possibilité très intéressante : demander au flux de “prévenir” en cas de problème.
Cela est effectué au moyen d’exceptions : le flux lève une exception standard de la classe ios_base::failure
(dérivée de la classe standard exception
) dès qu’une opération échoue.
Le développeur n’a plus qu’à inclure son code dans un bloc try
et à prévoir un bloc catch
pour capturer l’exception et effectuer le traitement approprié sur le flux.
Mais ce n’est pas le comportement par défaut du flux : il faut l’informer en appelant sa fonction membre exceptions()
, à laquelle on passe en paramètre la “liste” (une combinaison de flags) des événements susceptibles de déclencher une exception.
Ces événements possibles sont au nombre de 3 :
- “fin-de-fichier”, indiqué par le flag
ios_base::eofbit
, - un échec d’opération de lecture (extraction) ou d’écriture (injection), indiqué par le flag
ios_base::failbit
, - un échec d’opération dû à un fichier corrompu (en principe problème matériel), indiqué par le flag
ios_base::badbit
.
L’extrait de code suivant montre comment le flux standard d’entrée cin
est informé qu’il doit lever une exception dans l’un des deux premiers cas :
cin.exceptions (ios_base::failbit | ios_base::eofbit);
Créez le projet Failure
.
Dans le projet Failure
, copiez le contenu des fichiers
CstCodErr.h
:
/** * * \file CstCodErr.h * * \authors M. Laporte, D. Mathieu * * \date 10/02/2011 * * \version V1.0 * * \brief Codes d'erreurs * **/ #ifndef __CSTCODERR_H__ #define __CSTCODERR_H__ namespace nsUtil { enum {KNoExc = 0, KNoError = 0, KExcStd = 254, KExcInconnue = 255 }; } // namespace nsUtil #endif /* __CSTCODERR_H__ */
CException.h
:
/** * * \file CException.h * * \authors M. Laporte, D. Mathieu * * \date 10/02/2011 * * \version V1.0 * * \brief Declaration de la classe CException * **/ #ifndef __CEXCEPTION_H__ #define __CEXCEPTION_H__ #include <exception> #include <string> #include "CstCodErr.h" namespace nsUtil { class CException : public std::exception { std::string myLibelle; unsigned myCodErr; public : CException (const std::string & libelle = std::string(), const unsigned codErr = KNoExc) noexcept; virtual ~CException (void) noexcept; const std::string & getLibelle (void) const noexcept; unsigned getCodErr (void) const noexcept; virtual const char* what (void) const noexcept; void display (void) const; }; // CException } // namespace nsUtil #endif /* __CEXCEPTION_H__ */
CException.cpp
:
/** * * \file CException.cpp * * \authors M. Laporte, D. Mathieu * * \date 10/02/2011 * * \version V1.0 * * \brief classe CException * **/ #include <string> #include <iostream> #include "CstCodErr.h" #include "CException.h" using namespace std; #define CEXC nsUtil::CException //========================== // Classe nsUtil::CException //========================== CEXC::CException (const string & libelle /* = string () */, const unsigned codErr /* = KNoExc */) noexcept : myLibelle (libelle), myCodErr (codErr) {} const string & CEXC::getLibelle (void) const noexcept { return myLibelle; } // GetLibelle() unsigned CEXC::getCodErr (void) const noexcept { return myCodErr; } CEXC::~CException (void) noexcept {} const char* CEXC::what (void) const noexcept { return myLibelle.c_str(); } void CEXC::display (void) const { cout << "Exception : " << myLibelle << '\n' << "Code : " << myCodErr << endl; } // Afficher() #undef CEXC
et
SqueletteMain.cpp :
/** * * \file : SqueletteMain.cpp * * \author : * * \date : * **/ #include <iostream> #include <exception> #include "CstCodErr.h" #include "CException.h" using namespace std; using namespace nsUtil; namespace { void testFailure (void) { //TODO } // TestFailure () } // namespace int main (void) { try { testFailure (); return KNoExc; } catch (const CException & e) { cerr << "Erreur : " << e.getLibelle () << '\n' << "Code d'erreur = " << e.getCodErr () << '\n'; return e.getCodErr(); } catch (const exception & e) { cerr << "Exception standard : " << e.what () << '\n'; return KExcStd; } catch (...) { cerr << "Exception inconnue\n"; return KExcInconnue; } } // main()
Renommer SqueletteMain.cpp
en TestFailure.cpp
.
Dans la fonction TestFailure()
du fichier TestFailure.cpp
, positionner le flux standard d’entrée pour qu’il lève une exception en cas de fin de fichier et d’échec d’extraction.
Ajouter un bloc try
contenant une boucle infinie de lecture d’un entier au clavier.
Ajouter un bloc catch
capturant l’exception levée par le flux, l’afficher (fonction membre what()
), puis consulter l’état du flux (fail()
ou eof()
) pour afficher le diagnostic d’arrêt de la boucle.
Après cet affichage, propager l’exception (throw
tout court).
Vous devez constater que vous capturez à nouveau cette exception dans la fonction main()
.
Ne pas oublier de sauvegarder les fichiers sources du projet sur github
.
M2103-TP4-Exo-2-Corrigé
/** * * \file TestFailure.cpp * * \author D. Mathieu, M. Laporte * * \date 10/02/2011 * **/ #include <iostream> #include "CstCodErr.h" #include "CException.h" using namespace std; using namespace nsUtil; namespace { void testFailure (void) { cout << "testFailure\n\n"; cin.exceptions (ios_base::failbit | ios_base::eofbit); try { int i; for (;;) { cout << "Un entier : "; cin >> i; } } catch (const ios_base::failure & exc) { if (cin.eof ()) cerr << "Fin de fichier\n"; else if (cin.fail ()) cerr << "Erreur de lecture\n"; cerr << exc.what () << '\n'; throw; } } // testFailure() } // namespace int main (void) { try { testFailure (); return KNoExc; } catch (const CException & e) { cerr << "Erreur : " << e.getLibelle () << '\n' << "Code d'erreur = " << e.getCodErr () << '\n'; return e.getCodErr (); } catch (const exception & e) { cerr << "Exception standard : " << e.what () << '\n'; return KExcStd; } catch (...) { cerr << "Exception inconnue\n"; return KExcInconnue; } } // main()
M2103-TP4-Exo-3
Créez le projet DivisionParZero
Failure
, copiez-y le contenu des fichiers
CstCodErr.h
:
/** * * \file CstCodErr.h * * \authors M. Laporte, D. Mathieu * * \date 10/02/2011 * * \version V1.0 * * \brief Codes d'erreurs * **/ #ifndef __CSTCODERR_H__ #define __CSTCODERR_H__ namespace nsUtil { enum {KNoExc = 0, KNoError = 0, KExcStd = 254, KExcInconnue = 255 }; } // namespace nsUtil #endif /* __CSTCODERR_H__ */
CException.h
:
/** * * \file CException.h * * \authors M. Laporte, D. Mathieu * * \date 10/02/2011 * * \version V1.0 * * \brief Declaration de la classe CException * **/ #ifndef __CEXCEPTION_H__ #define __CEXCEPTION_H__ #include <string> #include <exception> #include "CstCodErr.h" namespace nsUtil { class CException : public std::exception { std::string myLibelle; unsigned myCodErr; public : CException (const std::string & libelle = std::string(), const unsigned codErr = KNoExc) noexcept; virtual ~CException (void) noexcept; const std::string & getLibelle (void) const noexcept; unsigned getCodErr (void) const noexcept; virtual const char* what (void) const noexcept; void display (void) const; }; // CException } // namespace nsUtil #endif /* __CEXCEPTION_H__ */
CException.cpp
:
/** * * \file CException.cpp * * \authors M. Laporte, D. Mathieu * * \date 10/02/2011 * * \version V1.0 * * \brief classe CException * **/ #include <iostream> #include <string> #include "CstCodErr.h" #include "CException.h" using namespace std; #define CEXC nsUtil::CException //========================== // Classe nsUtil::CException //========================== CEXC::CException (const string & libelle /* = string () */, const unsigned codErr /* = KNoExc */) noexcept : myLibelle (libelle), myCodErr (codErr) {} const string & CEXC::getLibelle (void) const noexcept { return myLibelle; } // GetLibelle() unsigned CEXC::getCodErr (void) const noexcept { return myCodErr; } CEXC::~CException (void) noexcept {} const char* CEXC::what (void) const noexcept { return myLibelle.c_str(); } void CEXC::display (void) const { cout << "Exception : " << myLibelle << '\n' << "Code : " << myCodErr << endl; } // Afficher() #undef CEXC
et SqueletteMain.cpp
/** * * \file : SqueletteMain.cpp * * \author : * * \date : * **/ #include <iostream> #include <exception> #include "CstCodErr.h" #include "CException.h" using namespace std; using namespace nsUtil; namespace { void testDivisionParZero (void) { //TODO } // testDivisionParZero () } // namespace int main (void) { try { testDivisionParZero (); return KNoExc; } catch (const CException & e) { cerr << "Erreur : " << e.getLibelle () << '\n' << "Code d'erreur = " << e.getCodErr () << '\n'; return e.getCodErr(); } catch (const exception & e) { cerr << "Exception standard : " << e.what () << '\n'; return KExcStd; } catch (...) { cerr << "Exception inconnue\n"; return KExcInconnue; } } // main()
Renommer SqueletteMain.cpp
en DivisionParZero.cpp
.
Dans l’espace de noms anonyme du fichier DivisionParZero.cpp
, écrire la fonction divisionEntiere()
qui effectue la division du premier paramètre par le second.
En cas de division par zéro, elle lève une CException
de code d’erreur CstExcDivZero
(à ajouter dans CstCodErr
) accompagné d’un message d’erreur adéquat.
En fin de profil de la fonction divisionEntiere()
, ajouter throw (CException)
pour indiquer à l’utilisateur qu’elle risque de lever ce type d’exception.
Dans la fonction divisionParZero()
du fichier DivisionParZero.cpp
, déclarer un tableau de numérateurs et un tableau de dénominateurs (dont au moins 1 nul).
Dans une boucle, effectuer toutes les divisions possibles des couples {numérateur / dénominateur} de même indice et afficher un message d’erreur en cas de division par zéro.
Ne pas oublier de sauvegarder les fichiers sources du projet sur github
.
M2103-TP4-Exo-3-Corrigé
/** * * \file CstCodErr.h * * \authors M. Laporte, D. Mathieu * * \date 10/02/2011 * * \version V1.0 * * \brief Codes d'erreurs * **/ #ifndef __CSTCODERR_H__ #define __CSTCODERR_H__ namespace nsUtil { enum {KNoExc = 0, KNoError = 0, KExcDivZero = 11, // Division par zero KExcStd = 254, KExcInconnue = 255 }; } // namespace nsUtil #endif /* __CSTCODERR_H__ */ /** * * \file DivisionParZero.cxx * * \author D. Mathieu * * \date 07/12/2011 * **/ #include <iostream> #include <exception> #include <iomanip> // setw() #include "CstCodErr.h" #include "CException.h" using namespace std; using namespace nsUtil; namespace { int divisionEntiere (int num, int denom) throw (CException) { if (0 == denom) throw CException ("Division par zero", KExcDivZero); return num / denom; } // divisionEntiere() void divisionParZero () { int lesNums [] = { 12, 3, -5, 0, 40 }; const unsigned KSzFractions = sizeof (lesNums) / sizeof (lesNums [0]); int lesDenoms [KSzFractions] = { 4, 0, -5, 10, 4 }; for (unsigned i = 0; i < KSzFractions; ++i) { cout << setw (4) << lesNums [i] << " / " << setw (4) << lesDenoms [i] << " = "; try { cout << divisionEntiere (lesNums [i], lesDenoms [i]) << '\n'; } catch (const CException & e) { cout << "Erreur : " << e.getLibelle() << "; Code d'erreur = " << e.getCodErr() << '\n'; } } } // divisionParZero() } // namespace int main () { try { divisionParZero(); return KNoExc; } catch (const CException & e) { cerr << "Erreur : " << e.getLibelle() << '\n' << "Code d'erreur = " << e.getCodErr() << '\n'; return e.getCodErr(); } catch (const exception & e) { cerr << "Exception standard : " << e.what() << '\n'; return KExcStd; } catch (...) { cerr << "Exception inconnue\n"; return KExcInconnue; } } // main()
M2103-TP4-Exo-4
Créez le projet RationnelV2
.
copiez-y le contenu des fichiers
CstCodErr.h
:
/** * * \file CstCodErr.h * * \authors M. Laporte, D. Mathieu * * \date 10/02/2011 * * \version V1.0 * * \brief Codes d'erreurs * **/ #ifndef __CSTCODERR_H__ #define __CSTCODERR_H__ namespace nsUtil { enum {KNoExc = 0, KNoError = 0, KExcStd = 254, KExcInconnue = 255 }; } // namespace nsUtil #endif /* __CSTCODERR_H__ */
CException.h
:
/** * * \file CException.h * * \authors M. Laporte, D. Mathieu * * \date 10/02/2011 * * \version V1.0 * * \brief Declaration de la classe CException * **/ #ifndef __CEXCEPTION_H__ #define __CEXCEPTION_H__ #include <string> #include <exception> #include "CstCodErr.h" namespace nsUtil { class CException : public std::exception { std::string myLibelle; unsigned myCodErr; public : CException (const std::string & libelle = std::string(), const unsigned codErr = KNoExc) noexcept; virtual ~CException (void) noexcept; const std::string & getLibelle (void) const noexcept; unsigned getCodErr (void) const noexcept; virtual const char* what (void) const noexcept; void display (void) const; }; // CException } // namespace nsUtil #endif /* __CEXCEPTION_H__ */
CException.cpp
:
/** * * \file CException.cpp * * \authors M. Laporte, D. Mathieu * * \date 10/02/2011 * * \version V1.0 * * \brief classe CException * **/ #include <string> #include <iostream> #include "CstCodErr.h" #include "CException.h" using namespace std; #define CEXC nsUtil::CException //========================== // Classe nsUtil::CException //========================== CEXC::CException (const string & libelle /* = string () */, const unsigned codErr /* = KNoExc */) noexcept : myLibelle (libelle), myCodErr (codErr) {} const string & CEXC::getLibelle (void) const noexcept { return myLibelle; } // GetLibelle() unsigned CEXC::getCodErr (void) const noexcept { return myCodErr; } CEXC::~CException (void) noexcept {} const char* CEXC::what (void) const noexcept { return myLibelle.c_str(); } void CEXC::display (void) const { cout << "Exception : " << myLibelle << '\n' << "Code : " << myCodErr << endl; } // Afficher() #undef CEXC
SqueletteMain.cpp
:
/** * * \file : SqueletteMain.cpp * * \author : * * \date : * **/ #include <iostream> #include <exception> #include "CstCodErr.h" #include "CException.h" using namespace std; using namespace nsUtil; namespace { void testDivisionParZero (void) { //TODO } // testDivisionParZero () } // namespace int main (void) { try { testDivisionParZero (); return KNoExc; } catch (const CException & e) { cerr << "Erreur : " << e.getLibelle () << '\n' << "Code d'erreur = " << e.getCodErr () << '\n'; return e.getCodErr(); } catch (const exception & e) { cerr << "Exception standard : " << e.what () << '\n'; return KExcStd; } catch (...) { cerr << "Exception inconnue\n"; return KExcInconnue; } } // main()
Rationnel.h
:
/** * * \file Rationnel.h * * \authors M. Laporte, D. Mathieu * * \date 01/02/2008 * * \version V1.0 * * \brief Declaration de la classe Rationnel (V1) * Ajout des operateurs de relation * **/ #ifndef __RATIONNEL_H__ #define __RATIONNEL_H__ namespace nsMath { class Rationnel { int myNum; int myDenom; void simplify (void); public : Rationnel (const int num = 0, const int denom = 1); Rationnel (const Rationnel & r); void display (void) const; bool operator < (const Rationnel & r) const; bool operator == (const Rationnel & r) const; Rationnel operator + (const Rationnel & r) const; Rationnel operator - (const Rationnel & r) const; Rationnel operator * (const Rationnel & r) const; Rationnel operator / (const Rationnel & r) const; }; // Rationnel } // namespace nsMath #endif /* __RATIONNEL_H__ */
et Rationnel.cpp
:
/** * * \file Rationnel.cpp * * \authors M. Laporte, D. Mathieu * * \date 07/12/2011 * * \version V1.0 * * \brief Definition des methodes de la classe Rationnel * (version 1) * **/ #include <iostream> #include <cmath> // abs() #include "Rationnel.h" #define RATIONNEL nsMath::Rationnel using namespace std; using namespace nsMath; namespace { /* unsigned PGDC (const unsigned a, const unsigned b) { if (a == b) return a; if (a < b) return PGDC (a, b - a); if (a > b) return PGDC (b, a - b); } // PGDC() */ unsigned PGDC (unsigned a, unsigned b) { for ( ; a != b; ) { if (a < b) b -= a; else a -= b; } return a; } // PGDC() } // namespace RATIONNEL::Rationnel (const int num /* = 0 */, const int denom /* = 1 */) : myNum (num), myDenom (denom) { simplify (); } // Rationnel() RATIONNEL::Rationnel (const Rationnel & r) : myNum (r.myNum), myDenom (r.myDenom) {} void RATIONNEL::display (void) const { cout << myNum << '/' << myDenom; } // display() void RATIONNEL::simplify (void) { if (myDenom < 0) { myNum = -myNum; myDenom = -myDenom; } int pgdc = (myNum == 0) ? myDenom : PGDC (abs (myNum), abs (myDenom)); myNum /= pgdc; myDenom /= pgdc; } // simplify() bool RATIONNEL::operator < (const Rationnel & r) const { return myNum * r.myDenom < myDenom * r.myNum; } // operator < bool RATIONNEL::operator == (const Rationnel & r) const { return myNum == r.myNum && myDenom == r.myDenom; } // operator == RATIONNEL RATIONNEL::operator + (const Rationnel & r) const { return Rationnel (myNum * r.myDenom + r.myNum * myDenom, myDenom * r.myDenom); } // operator + RATIONNEL RATIONNEL::operator - (const Rationnel & r) const { return Rationnel (myNum * r.myDenom - r.myNum * myDenom, myDenom * r.myDenom); } // operator - RATIONNEL RATIONNEL::operator * (const Rationnel & r) const { return Rationnel (myNum * r.myNum, myDenom * r.myDenom); } // operator * RATIONNEL RATIONNEL::operator / (const Rationnel & r) const { return Rationnel (myNum * r.myDenom, myDenom * r.myNum); } // operator / #undef RATIONNEL
Renommer SqueletteMain.cpp
en TestCRationnel.cpp
.
Modifiez les fonctions membres susceptibles d’effectuer des divisions par zéro en :
- ajoutant
throw (CException)
à leur profil, - levant une
CException
de code d’erreurCstExcDivZero
accompagné d’un message d’erreur adéquat chaque fois qu’une division par zéro est détectée,
Dans la fonction TestCRationnel()
du fichier TestCRationnel.cpp
, tester les différents cas d’exceptions.
M2103-TP4-Exo-4-Corrigé
/** * * \file Rationnel.h * * \authors M. Laporte, D. Mathieu * * \date 06/11/2007 * * \version V1.0 * * \brief Declaration de la classe Rationnel - V2 * Gestion des exceptions * **/ #ifndef __RATIONNEL_H__ #define __RATIONNEL_H__ #include "CException.h" typedef unsigned long long ULLong_t; namespace nsMath { class Rationnel { int myNum; int myDenom; void Simplifier (void) noexcept; public : Rationnel (const int num = 0, const int denom = 1) throw (nsUtil::CException); Rationnel (const Rationnel & r) noexcept; void display (void) const; bool operator < (const Rationnel & r) const noexcept; bool operator == (const Rationnel & r) const noexcept; Rationnel operator + (const Rationnel & r) const noexcept; Rationnel operator - (const Rationnel & r) const noexcept; Rationnel operator * (const Rationnel & r) const noexcept; Rationnel operator / (const Rationnel & r) const throw (nsUtil::CException); }; // Rationnel } // namespace nsMath #endif /* __RATIONNEL_H__ */ /** * * \file Rationnel.cpp * * \authors M. Laporte, D. Mathieu * * \date 07/12/2011 * * \version V1.0 * * \brief Definition des methodes de la classe Rationnel (V2) * Gestion des exceptions * **/ #include <iostream> #include <exception> #include <cmath> // abs() #include "Rationnel.h" #include "CstCodErr.h" #include "CException.h" #define RATIONNEL nsMath::Rationnel using namespace std; using namespace nsUtil; using namespace nsMath; namespace { unsigned PGDC (unsigned a, unsigned b) noexcept { for ( ; a != b; ) { if (a < b) b -= a; else a -= b; } return a; } // PGDC() } // namespace RATIONNEL::Rationnel (const int num /* = 0 */, const int denom /* = 1 */) throw (CException) : myNum (num), myDenom (denom) { if (myDenom == 0) throw CException ("Diviseur nul", KExcDivZero); Simplifier (); } // Rationnel() RATIONNEL::Rationnel (const Rationnel & r) noexcept : myNum (r.myNum), myDenom (r.myDenom) {} void RATIONNEL::display (void) const { cout << myNum << '/' << myDenom; } // display() void RATIONNEL::Simplifier (void) noexcept { if (myDenom < 0) { myNum = -myNum; myDenom = -myDenom; } int pgdc = (myNum == 0) ? myDenom : PGDC (abs (myNum), abs (myDenom)); myNum /= pgdc; myDenom /= pgdc; } // Simplifier() bool RATIONNEL::operator < (const Rationnel & r) const noexcept { return myNum * r.myDenom < myDenom * r.myNum; } // operator < bool RATIONNEL::operator == (const Rationnel & r) const noexcept { return myNum == r.myNum && myDenom == r.myDenom; } // operator == RATIONNEL RATIONNEL::operator + (const Rationnel & r) const noexcept { return Rationnel (myNum * r.myDenom + r.myNum * myDenom, myDenom * r.myDenom); } // operator + RATIONNEL RATIONNEL::operator - (const Rationnel & r) const noexcept { return Rationnel (myNum * r.myDenom - r.myNum * myDenom, myDenom * r.myDenom); } // operator - RATIONNEL RATIONNEL::operator * (const Rationnel & r) const noexcept { return Rationnel (myNum * r.myNum, myDenom * r.myDenom); } // operator * RATIONNEL RATIONNEL::operator / (const Rationnel & r) const throw (CException) { if (r.myNum == 0) throw CException ("Division par zero", KExcDivZero); return Rationnel (myNum * r.myDenom, myDenom * r.myNum); } // operator / #undef CRATIONNEL /** * * \file TestRationnel.cxx * * \authors M. Laporte, D. Mathieu * * \date 07/12/2011 * * \version V1.0 * * \brief Test de la classe Rationnel (V2) * Gestion des exceptions * **/ #include <iostream> #include <exception> #include "Rationnel.h" using namespace std; using namespace nsUtil; using namespace nsMath; namespace { void testRationnel (void) { try { cout << "Rationnel R1 (12, 0)\n"; Rationnel r1 (12, 0); cout << "R1 = "; r1.display (); cout << '\n'; } catch (const CException & e) { cout << "Erreur : " << e.getLibelle () << "; code d'erreur = " << e.getCodErr () << '\n'; } try { Rationnel r1 (4, 12); cout << "R1 = "; r1.display (); cout << '\n'; Rationnel r2 (0, 12); cout << "R2 = "; r2.display (); cout << '\n'; r1.display(); cout << " / "; r2.display(); cout << " = "; (r1 / r2).display (); cout << '\n'; } catch (const CException & e) { cout << "Erreur : " << e.getLibelle () << "; code d'erreur = " << e.getCodErr () << '\n'; } } // testRationnel() } // namespace int main (void) { try { testRationnel (); } catch (const CException & e) { cerr << "Erreur : " << e.getLibelle () << '\n' << "Code d'erreur = " << e.getCodErr () << '\n'; return e.getCodErr(); } catch (const exception & e) { cerr << "Exception standard : " << e.what () << '\n'; return KExcStd; } catch (...) { cerr << "Exception inconnue\n"; return KExcInconnue; } return KNoExc; } // main()