Série 20 (niveau 0) :
Héritage
Exercice 0 : exemple simple (héritage, niveau 0)
Introduction
Le but de cet exercice est de reprendre l'exemple du cours illustrant la notion d'héritage en utilisant une hiérarchie de figures géométriques.
Dans le fichier figures.cc, commencez par recopier les définitions des classe Rectangle et Cercle ci-dessous :
#include <cmath> using namespace std; // ---------------------------------------------------------------------- class Rectangle { private: double largeur; double longueur; public: double surface () const { return largeur * longueur; } double getLongueur() const { return longueur; } double getLargeur() const { return largeur; } void setLargeur(double l) { largeur = l; } void setLongueur(double l) { longueur = l; } }; // ---------------------------------------------------------------------- class Cercle { public: double surface() const { return M_PI * rayon * rayon; } bool estInterieur(const double x, const double y) const { return (((x-this->x) * (x-this->x) + (y-this->y) * (y-this->y)) <= rayon * rayon); } void getCentre(double &x, double &y) const { x = this->x; y = this->y; } void setCentre(const double x, const double y) { this->x = x; this->y = y; } double getRayon() const { return rayon; } void setRayon(double r) { if (r < 0.0) r = 0.0; rayon = r; } private: double rayon; double x; // abscisse du centre double y; // ordonnée du centre };
Notez tout de même que normalement en programmation, on évite de faire cela
(copier-coller) et on préfèrerait utiliser la compilation séparée (faire plusieurs
fichiers) !
Mais bon, ce n'est pas le sujet du jour...
Commençons par ajouter la classe « rectangle coloré » qui hérite de
rectangle.
Cette classe doit simplement avoir un attribut de plus, couleur,
disons de type unsigned int.
[Essayez de le faire par vous même avant de regarder la solution qui suit]
Ceci se fait très simplement :
- la classe RectangleColore hérite de Rectangle :
class RectangleColore : Rectangle { };
On préfèrera l'héritage « public », beaucoup plus "naturel" (et justifié ici) que l'héritage « private » par défaut :
class RectangleColore : public Rectangle { };
- et a un attribut de plus :
class RectangleColore : public Rectangle { protected: unsigned int couleur; };
- mais il ne faut pas oublier de récupérer les attributs et méthodes que l'on souhaite
hériter de Rectangle. Il faut pour cela changer leur statut de
private à protected :
class Rectangle { protected: double largeur; double longueur; public: // ... comme avant };
Pour rendre ces classes un peu plus réalistes (et utilisables) ajoutons leur à chacune un constructeur:
class Rectangle { protected: double largeur; double longueur; public: Rectangle(double larg, double L) : largeur(larg), longueur(L) {} // ... comme avant ... class RectangleColore : public Rectangle { protected: unsigned int couleur; public: RectangleColore(double larg, double L, unsigned int c) : Rectangle(larg, L), couleur(c) {} };
Testez avec ce main simpliste :
int main() { RectangleColore r(4.3, 12.5, 4); cout << r.getLargeur() << endl; return 0; }
Continuez en définissant les classes suivantes :
- La classe Figure, contenant deux attributs x et y (représentant le centre de la figure), une méthode affiche(ostream&) qui affiche simplement les coordonnées du centre et un constructeur prenant les coordonnées du centre.
- et faites hériter les classes Cercle et Rectangle de la classe Figure
(il faut donc modifier en particulier la classe Cercle)
[Essayez de le faire par vous même avant de regarder la solution qui suit]
Pour la classe Figure, rien de particulier, et il suffit de déplacer les définitions correspondantes de la classe Cercle à la classe Figure (donc les supprimer de Cercle) :
class Figure { protected: double x; // abscisse du centre double y; // ordonnée du centre public: void getCentre(double &x, double &y) const { x = this->x; y = this->y; } void setCentre(const double x, const double y) { this->x = x; this->y = y; } };
puis on ajoute la méthode affiche et le constructeur (rien de spécial ici) :
class Figure { protected: double x; // abscisse du centre double y; // ordonnée du centre public: Figure(double x =0.0, double y =0.0) { this->x = x; this->y=y; } void affiche(ostream& sortie) { sortie << "centre = (" << x << ", " << y << ")"; } //... suite comme avant };
Venons en à l'héritage proprement dit. Il suffit simplement d'ajouter la « marque » de l'héritage aux deux classes concernées.
class Rectangle : public Figure { ... }; class Cercle : public Figure { .... };
On peut maintenant tester que l'on hérite bien des propriétés de la classe Figure :
int main() { RectangleColore r(4.3, 12.5, 4); cout << r.getLargeur() << endl; r.affiche(cout); cout << endl; Cercle c; c.setCentre(2.3, 4.5); c.setRayon(12.2); c.affiche(cout); cout << endl; return 0; }
Si vous êtes perdus, voici ici le code complet à ce stade.
[Testez ce programme : compilez le, exécutez le. Essayez ensuite d'autres utilisations pour bien comprendre. ]
Évidemment pour être plus intéressantes, il faudrait répercuter les possibilités de la classe Figure au niveau des constructeurs des classes Rectangle et Cercle, et éventuellement enrichir la méthode affiche.
Pour les constructeurs c'est assez facile :
class Rectangle : public Figure { ... Rectangle(double larg, double L, double x, double u) : Figure(x,y), largeur(larg), longueur(L) {}; ... }; class Cercle : public Figure { public: Cercle(double rayon, double x = 0.0, double y = 0.0) : Figure(x,y), rayon(rayon) {} ... };
Pour la méthode affiche c'est un peu plus subtile. Supposons que l'on souhaite que pour la classe Cercle la méthode affiche également le rayon (mais continue d'afficher le centre).
On veut donc faire quelque chose comme (syntaxe fantaisiste) :
« cercle.affiche() = figure.affiche() + { cout << rayon } »
Cela se fait exactement de cette façon mais en utilisant la syntaxe du C++ et plus exactement l'opérateur de résolution de portée :
void Cercle::affiche(ostream& sortie) { Figure::affiche(sortie); sortie << ", r=" << rayon; }
et ne pas oublier de définir la méthode dans la classe Cercle :
class Cercle : public Figure { public: Cercle(double rayon, double x = 0.0, double y = 0.0) : Figure(x,y), rayon(rayon) {} void affiche(ostream&); ... };
On peut bien sûr faire de même avec la classe Rectangle :
void Rectangle::affiche(ostream& sortie) { Figure::affiche(sortie); sortie << ", largeur=" << largeur << ", longueur=" << longueur; }
int main() { RectangleColore r(4.3, 12.5, 4); cout << r.getLargeur() << endl; r.affiche(cout); cout << endl; Cercle c(12.2, 2.3, 4.5); c.affiche(cout); cout << endl; Rectangle r2(1.2, 3.4, 12.3, 43.2); r2.affiche(cout); cout << endl; return 0; }
[ici le code à ce stade]
Vous pouvez terminer l'exercice en ajoutant les classes Rectangle3D et Cylindre et les différentes versions de la méthode surface.
Cela se fait exactement comme la méthode affiche ci-dessus.