/** * * \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()
Archives d’Auteur: alain
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()
M2103-TP3-Exo-1
Créez le projet CException
.
Dans les fichiers CException.h
et CException.cpp
, ajouter dans l’espace de noms nsUtil
la classe CException
, dérivée de la classe standard exception
, possédant deux attributs (données membres) :
myLibelle
: un texte de typestring
contenant un diagnostic d’erreur,myCodErr
: un entier non signé,
et les méthodes suivantes :
- un constructeur à deux paramètres (le libellé de l’exception et le code de l’erreur), avec d’éventuelles valeurs par défaut,
- un destructeur virtuel (vide), nécessaire car la classe mère (
exception
) est elle-même virtuelle, - deux accesseurs permettant de récupérer les valeurs des données membres,
- une récriture de la fonction virtuelle
what()
, héritée de la classe mèreexception
, de profil suivant :virtual const char* what() const noexcept;
qui, au moyen de la méthode
c_str()
de la classestring
, renvoie le libellé de l’exception sous la forme d’uneNTCTS
(dont le type estconst char*
). - la fonction d’affichage (provisoire)
diplay()
, qui affiche à l’écran le contenu de l’exception, par exemple dans le format suivant :Exception : xxxxxxxxxxxxxxxxxxxxxxxxxxxx Code : yyyyy
Dans la fonction testException()
de l’espace de noms anonyme du fichier testCException.cpp
, écrire une boucle terminée par la fin de fichier cin
, constituée :
- de la saisie d’une chaîne de caractères (le libellé de l’exception),
- de la saisie d’un entier (le code d’erreur associé),
- de la construction d’une
CException
, - de son affichage.
Compiler et tester.
M2103-TP3-Exo-2
La classe standard string
est une classe très riche et indispensable et il est heureux qu’elle existe !
Cependant, on peut lui reprocher l’absence de certaines fonctionnalités, que l’on aimerait bien y trouver.
On se souvient, par exemple, de la mise en majuscules ou en minuscules.
Une première solution est d’écrire des “moulinettes”, fonctions prenant comme paramètre donnée (ou donnée-résultat) la string
et la rendant transformée.
C’est ce que nous avons fait précédemment (au premier semestre).
La seconde solution consiste à dériver la classe standard string
en une classe CString
à laquelle ne sont ajoutées aucun attribut (aucune donnée membre), mais seulement des méthodes (fonctions-membres).
Les seules fonctions qui ne sont pas héritées de la classe mère sont ses constructeurs, son destructeur, ainsi que l’affectation.
En revanche, un constructeur par recopie, un constructeur par défaut, un destructeur et une affectation sont données à la nouvelle classe (comme à chaque nouvelle classe), par le compilateur.
En ce qui concerne le constructeur par recopie, l’affectation et le destructeur, ils ne sont pas notre problème pour le moment.
Leur récriture sera abordée en temps utile.
La classe string
possède de nombreux constructeurs ayant chacun leur utilité.
Ne posséder qu’un seul constructeur CString()
qui se contente d’appeler le constructeur de sa classe mère, string()
, qui lui-même crée une chaîne vide, est un peu frustrant.
C’est pourquoi on dotera la classe CString
des mêmes constructeurs que sa classe mère.
Créez le projet CString
.
Copier / coller le code suivant :
/** * * \file TestCString.cpp * * \authors M. Laporte, D. Mathieu * * \date 05/11/2007 * * \version V1.0 * * \brief Test de la classe String, extension de String * **/ #include <iostream> #include "CString.h" using namespace std; using namespace nsUtil; namespace { void testString (void) { cout << "Test des constructeurs\n\n"; CString s1; cout << '"' << s1 << "\"\n"; cout << s1.append ("s1") << '\n'; CString s2 ("Coucou"); cout << (s2 + ' ' + s1) << '\n'; CString s3 ("Coucou", 4); cout << s3 << '\n'; CString s4 (10, '#'); cout << s4 << '\n'; CString s5 ("AbCdEf1234"); cout << s5 << '\n'; cout << s5.toupper() << '\n'; cout << s5 << '\n'; cout << s5.tolower() << '\n'; cout << s5 << '\n'; cout << "\nTest des fonctions\n\n"; CString STR ("MAJUSCULE"); CString str ("minuscule"); CString space (" \t\t\n\n"); cout << boolalpha; cout << str << " alpha ? " << str.isalpha () << '\n'; cout << str << " alnum ? " << str.isalnum () << '\n'; cout << str << " digit ? " << str.isdigit () << '\n'; cout << str << " xdigit ? " << str.isxdigit () << '\n'; cout << str << " lower ? " << str.islower () << '\n'; cout << str << " upper ? " << str.isupper () << '\n'; cout << str << " space ? " << str.isspace () << '\n'; cout << STR << " upper ? " << STR.isupper () << '\n'; cout << str << " lower ? " << str.islower () << '\n'; cout << space << " space ? " << space.isspace () << '\n'; cout << noboolalpha; } // testString () } // namespace int main (void) { testString(); return 0; } // main()
Dans l’espace de noms nsUtil
des fichiers CString.h
et CString.cpp
, dériver publiquement la classe string
(afin de garder et de transmettre la visibilité sur les fonctions membres de la classe mère).
Lui ajouter les constructeurs ayant les mêmes profils que les constructeurs suivants de la classe string
, et qui se contentent d’appeler ces derniers en leur passant les mêmes paramètres :
string (const char * NTCTS); string (const char * NTCTS, size_type size); string (const string & S, size_type pos = 0, size_type size = npos); string (size_type size, char c);
Ajouter les fonctions membres tolower()
et toupper()
, de même identificateurs que les fonctions standard tolower()
et toupper()
qui s’appliquent aux caractères.
Le paramètre désignant la chaîne à traiter n’a plus lieu d’être puisque les fonctions deviennent des méthodes de la classe CString
(fonctions-membres), donc appelées sur un objet donné.
Rappel : l’objet lui-même est désigné par (*this)
.
Ajoutez les prédicats isalpha()
, isdigit()
, isalnum()
, isxdigit()
, islower()
, isupper()
, isspace()
, extensions des mêmes fonctions standard existant pour les caractères.
Si vous les avez faites, ajoutez aussi les fonctions complémentaires qui vous avaient été proposées : leftAlign()
, compact()
, rightAlign()
, expandTab()
, justify()
.
M2103-TP2-Exo-1
Remarque préliminaire : cet exercice ne peut être effectué qu’après l’exercice 4 du TP 1 (constructeurs par défaut et par recopie, destructeur).
Récupérer les fichiers sources de l’exo 4 du TP précédent.
Supprimer les affichages intermédiaires dans les constructeurs et dans le destructeur.
Ajouter les surcharges des deux opérateurs +
et -
, membres de la classe, qui n’ont pour opérande(s) que des objets Duree
.
Si la soustraction est impossible (une Duree
est considérée comme invalide), le résultat doit être une durée par défaut (nulle).
Téléchargez le fichier de test TestCDuree.cpp
.
M2103-TP2-Exo-1-Corrigé
Duree.h
/** * * \file Duree.h * * \authors M. Laporte * * \date 02/04/2018 * * \version V2.0 * * \brief déclarations de la classe Duree (avec constructeurs et * destructeur) * **/ #ifndef __DUREE_H__ #define __DUREE_H__ namespace nsUtil { typedef unsigned long long ULLong_t; class Duree { private : ULLong_t myDuree; short unsigned mySeconds; short unsigned myMinutes; short unsigned myHours; ULLong_t myDays; void normaliser (void); public : Duree (const ULLong_t duree = ULLong_t (0)); Duree (const Duree & duree); ~Duree (void); ULLong_t getDuree (void) const; void display (void) const; void incr (const ULLong_t delta = ULLong_t (0)); void decr (const ULLong_t delta = ULLong_t (0)); Duree operator + (const Duree & d) const; Duree operator - (const Duree & d) const; }; // Duree } // nsUtil #endif /* __DUREE_H__ */
Duree.cpp
/** * * \file Duree.cpp * * \authors M. Laporte * * \date 02/04/2018 * * \version V2.0 * * \brief définitions de la classe Duree * **/ #include <iostream> #include <iomanip> // setw() #include "Duree.h" using namespace nsUtil; using namespace std; #define DUREE nsUtil::Duree DUREE::Duree (const ULLong_t duree /* = ULLong_t (0) */) : myDuree (duree) { normaliser (); } // Duree() DUREE::Duree (const Duree & duree) : myDuree (duree.getDuree ()) { normaliser (); } // Duree() DUREE::~Duree (void) { } void DUREE::normaliser (void) { myDays = myDuree / 86400; myHours = (myDuree % 86400) / 3600; myMinutes = (myDuree % 3600) / 60; mySeconds = myDuree % 60; } // normaliser() ULLong_t DUREE::getDuree (void) const { return myDuree; } void DUREE::display (void) const { cout << '[' << setw (10) << myDays << ':' << setfill ('0') << setw (2) << myHours << " heure(s)" << setw (2) << myMinutes << " minute(s)" << setw (2) << mySeconds << " seconde(s)" << setfill (' ') << ']'; } // display() void DUREE::incr (const ULLong_t delta /* = ULLong_t (0) */) { myDuree += delta; normaliser (); } // incr() void DUREE::decr (const ULLong_t delta /* = ULLong_t (0) */) { myDuree -= (delta > myDuree) ? myDuree : delta; } // decr() DUREE DUREE::operator + (const Duree & d) const { return myDuree + d.myDuree; } // operator +() DUREE DUREE::operator - (const Duree & d) const { return myDuree - (myDuree < d.myDuree ? myDuree : d.myDuree); } // operator -() #undef DUREE
M2103-TP2-Exo-2
Remarque préliminaire : cet exercice ne peut être effectué qu’après l’exercice 1 de ce même TP (opérateurs arithmétiques : +
et -)
.
Récupérer les fichiers sources de l’exercice précédent.
Téléchargez le fichier
TestDureeRelops.cpp
.
Opérateur >
A la classe Duree
, ajouter la surcharge de l’opérateur >
.
Compiler le fichier TestCDureeRelops.cpp
en mettant en commentaires les instructions contenant des erreurs.
Vous devez remarquer que >
reste le seul opérateur accessible.
Opérateur <
Dans la classe Duree
:
- remplacer l’opérateur
>
par l’opérateur<
, - inclure le fichier standard
<utility>
dans le fichierTestCDureeRelops.cpp
, - ajouter la clause
using namespace rel_ops
- réactiver toutes les instructions du fichier qui utilisent les opérateurs de comparaison,
- recompiler le fichier
TestCDureeRelops.cpp
et tester.
Vous remarquez alors que le seul ajout de <
rend possible l’utilisation des opérateurs <=
, >=
et >
.
Cela est dû au fait que le C++ propose des versions génériques de <=
, >=
et >
, définies à partir de <
et qui s’instancient automatiquement en cas de besoin.
Il est donc totalement inutile et en général mauvais de surcharger les trois autres opérateurs.
Il peut être surprenant de ne pas obtenir les opérateurs ==
et !=
à partir de <
, alors que c’est mathématiquement possible.
Mais c’est normal en informatique …
Opérateur !=
A la classe Duree
, ajouter la surcharge de l’opérateur !=
.
Compiler le fichier TestCDureeRelops.cxx
en mettant en commentaires les instructions contenant des erreurs.
Vous remarquez alors que seule l’inégalité est ajoutée.
Opérateur ==
Remplacez alors la surcharge de !=
par celle de ==
.
Normalement, toutes les instructions de TestCDureeRelops.cpp
passent à la compilation.
Le seul ajout de ==
rend possible l’utilisation de l’opérateur !=
.
Nous montrerons dans un qu’elle rend aussi possible l’utilisation de la fonction générique standard find()
(et bien d’autres !).
Pour conclure :
- une classe qui possède la surcharge de l’opérateur
<
est dite LessThanComparable.Elle possède alors une relation d’ordre. - une classe qui possède la surcharge de l’opérateur
==
est dite EqualityComparable.