INFO702 - TD rationnels
POO/surcharge via la construction d’une classe représentant les rationnels
[Jacques-Olivier Lachaud, August 2025]
L’objectif de cette série d’exercices est de construire progressivement une classe en C++ représentant les rationnels (Rational), en introduisant pas à pas les concepts de programmation orientée objet et de surcharge d’opérateurs.
Niveau 1 : Bases de la classe
Définir une classe
Rationalavec deux attributs privésnum(numérateur) etden(dénominateur).
Ajouter :- un constructeur prenant deux entiers
(p, q); - un constructeur par défaut représentant
0/1.
🤔 Pourquoi n’a-t-on pas besoin d’écrire un constructeur par copie ?
- un constructeur prenant deux entiers
Ajouter une méthode
toString()qui retourne une chaîne de caractères de la forme"num/den".
Tester la création et l’affichage de quelques rationnels.Ajouter deux accesseurs
n()etd()en lecture pour le numérateur et dénominateur.🤔 Pourquoi veut-on que
numetdensoit privé ? On pourrait juste les rendre publique et ne pas faire d’accesseur. Quel(s) serai(en)t le(s) défaut(s) de cette approche ?
Niveau 2 : Invariants et simplification
Modifier la classe pour que le signe soit toujours porté par le numérateur (le dénominateur doit rester positif).
Écrire une méthode statique privée
gcd(int a, int b)qui calcule le PGCD.
Ajouter une méthodereduce()qui simplifie le rationnel.
Appelerreduce()dans le constructeur.📝 On rappelle qu’on utilise la formule
gcd(a,b)=gcd(b, a % b)sib > a > 0etgcd(a,0)=asinon.⚠️ Faut-il faire attention au signe de a ou b dans la fonction
gcd? Si oui ou non, pourquoi ?🤔 Pourquoi veut-on réduire la fraction (e.g. 3/10) plutôt que de la laisser telle quelle (e.g 15/50) ? Y voyez-vous un ou des avantages ?
Niveau 3 : Surcharge d’opérateurs arithmétiques
Surcharger les opérateurs
+=,-=,*=,/=`.Pour rappel le prototype de la surcharge de l’opérateur
+est :class Rational { ... // en tant que méthode Rational& operator+=( const Rational& other ); ... };Exemple : On pourra vérifier que
Rational r(1,2); r += Rational(1,3); std::cout << r.toString() << "\n"; // == Rational(5,6)⚠️ Attention à la gestion de la division par zéro. On pourra aussi laisser l’état du rationnel à
1/0ou-1/0(si le numérateur n’était pas nul) ou0/0. Cela permet de distinguer si le rationnel est infini ou non-valide.Surcharger les opérateurs
+,-,*et/. .Pour rappel, le prototype de la surcharge de l’opérateur
+est :class Rational { ... // en tant que méthode Rational operator+( const Rational& other ) const; ... }; // en tant que fonction Rational operator+( const Rational& r1, const Rational& r2 ) const;Exemple : On pourra vérifier que
Rational(1,2) + Rational(1,3) == Rational(5,6).💡 On utilisera opportunément les surcharges précedentes.
Niveau 4 : Surcharge des opérateurs relationnels
Implémenter
operator==etoperator!=.Vérifier que
Rational(3,4) == Rational(9,12).Implémenter
<,<=,>,>=en comparant les produits croisés.
Exemple :Rational(1,2) < Rational(2,3).- Vérifier que la classe est compatible avec
std::sort.
- Vérifier que la classe est compatible avec
Niveau 5 : Opérateurs d’E/S et conversions
Surcharger
operator<<pour afficher un rationnel dans un fluxostream.
Surchargeroperator>>pour lire un rationnel depuis un fluxistream.⚠️ Ces opérateurs sont forcéments des fonctions, pas des méthodes. Par exemple, le prototype de
operator<<eststd::ostream& operator<<( std::ostream& out, const Rational& r );🤔 pourquoi ne peut-on écrire ces opérateurs comme méthodes ?
Ajouter un constructeur prenant un
int.
🤔 Peut-on maintenant écrire les lignes suivantes ?
Rational r(9,4); Rational s = r + 2; s += -2; Rational t = 3 + r;
- Ajouter un
operator double()pour convertir en flottant.x = double( Rational(4,3) )donne 1.333333334.
Niveau 6 : Construction d’un rationnel approchant un nombre réel
- On cherche à écrire une méthode statique
approx( double x, int den )qui construit le rationnel qui est la meilleure approximation dexde dénominateur inférieur ou égal àden. Comment faire ?
🤔 La notion de fraction continue sera explorée, voir aussi Fraction continue et approximation diophantienne.
Bonnes pratiques et extensions
Lever une exception si le dénominateur vaut zéro (dans le constructeur et dans
/).Rendre les méthodes et opérateurs
constlorsque c’est pertinent.Exercices bonus :
- Implémenter
operator++etoperator--(préfixe et suffixe). - Ecrire une version générique de
Rationaloù l’utilisateur peut choisir sont type d’entier (donc avoir une précision supérieure par exemple).
- Implémenter
💡 Objectif final : obtenir une classe Rational robuste, sûre et efficace, capable de s’intégrer dans un code C++ moderne.