Lien vers le sujet : https://github.com/IUTInfoAix-M2103/TP4
Archives du 11 avril 2018
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-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-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.