Exercice 1 | Exercice 2 |
Définissez une classe Forme
en la dotant d'une méthode
qui affiche [...]
#include <iostream> using namespace std; class Forme { public: void description() const { cout << "Ceci est une forme." << endl; } };
Ajoutez au programme une classe Cercle
héritant
de la classe Forme
, et possédant une méthode
void description()
qui affiche [...]
class Cercle : public Forme { public: void description() const { cout << "Ceci est un cercle." << endl; } };
[...] Testez ensuite à nouveau votre programme.
Voyez-vous vu la nuance ?
Pourquoi a-t-on ce
fonctionnement ?
La différence vient de ce que c.description();
appelle la méthode description()
de la classe
Cercle
(c'est-à-dire Cercle::description()
), alors que f2.description();
appelle celle de la classe Forme
(c'est-à-dire Forme::description()
), bien que elle ait été
construite par une copie d'un cercle.
Le polymorphisme n'opère pas ici car aucune des deux conditions nécessaires n'est remplie : la méthode n'est pas virtuelle et on ne passe par pas par des références ni pointeurs.
[...] Ajoutez encore au programme une fonction
void affichageDesc(Forme& f)
[...]
Le résultat vous semble-t-il
satisfaisant ?
Avec cette fonction, nous apportons une solution au second aspect puisqu'en effet nous passons l'argument par référence.
Le résultat n'est cependant toujours pas satisfaisant (c'est
toujours la méthode Forme::description()
qui est appelée) car le
premier problème subsiste : la méthode n'est pas virtuelle.
Modifiez le programme (ajoutez 1 seul mot) pour que le résultat soit plus conforme à ce que l'on pourrait attendre.
Il suffit donc d'ajouter virtual
devant le
prototype de la méthode description
de la classe
Forme
.
Voici le programme complet :
#include <iostream> using namespace std; class Forme { public: virtual void description() const { cout << "Ceci est une forme." << endl; } }; class Cercle : public Forme { public: void description() const { cout << "Ceci est un cercle." << endl; } }; void affichageDesc(const Forme& f) { f.description(); } int main() { Cercle c; affichageDesc(c); return 0; }
Modifiez la classe Forme
de manière à en faire
une classe abstraite [...]
class Forme { public: virtual void description() const { cout << "Ceci est une forme !" << endl; } virtual double aire() const = 0; };
Ce qui en fait une méthode virtuelle pure c'est le =0
derrière qui indique que pour cette classe cette méthode ne sera pas
implémentée (i.e. pas de définition, c.-à-d. pas de corps).
Écrivez une classe Triangle
et
modifiez la classe Cercle
existante héritant toutes
deux de la classe Forme
, et implémentant les méthodes
aire()
et description()
. [...]
class Cercle : public Forme { public: Cercle(double r = 0.0) : rayon(r) {} void description() const { cout << "Ceci est un cercle !" << endl; } double aire() const { return M_PI * rayon * rayon; } private: double rayon; }; class Triangle : public Forme { public: Triangle(double h = 0.0, double b = 0.0) : base(b), hauteur(h) {} void description() const { cout << "Ceci est un triangle !" << endl; } double aire() const { return 0.5 * base * hauteur; } private: double base; double hauteur; };
Modifiez la fonction affichageDesc
pour qu'elle
affiche, en
plus, l'aire [...]
void affichageDesc(Forme& f) { f.description(); cout << " son aire est " << f.aire() << endl; }
et le programme complet :
#include <iostream> #include <cmath> // pour M_PI using namespace std; class Forme { public: virtual void description() const { cout << "Ceci est une forme !" << endl; } virtual double aire() const = 0; }; class Cercle : public Forme { public: Cercle(double r = 0.0) : rayon(r) {} void description() const { cout << "Ceci est un cercle !" << endl; } double aire() const { return M_PI * rayon * rayon; } private: double rayon; }; class Triangle : public Forme { public: Triangle(double h = 0.0, double b = 0.0) : base(b), hauteur(h) {} void description() const { cout << "Ceci est un triangle !" << endl; } double aire() const { return 0.5 * base * hauteur; } private: double base; double hauteur; }; void affichageDesc(Forme& f) { f.description(); cout << " son aire est " << f.aire() << endl; } int main() { Cercle c(5); Triangle t(10, 2); affichageDesc(t); affichageDesc(c); return 0; }
qui donne comme résultat :
Ceci est un triangle ! son aire est 10 Ceci est un cercle ! son aire est 78.5398
#include <iostream> #include <array> #include <vector> #include <string> using namespace std; // ==================================================================== enum Couleur { vide = 0, rouge, jaune }; // ==================================================================== class Jeu { public: Jeu(size_t taille = 8); bool jouer(size_t, Couleur); Couleur gagnant() const; bool fini(Couleur&) const; size_t get_taille() const { return grille.size(); } ostream& affiche(ostream& out) const; protected: vector< vector< Couleur > > grille; private: unsigned int compte(Couleur, size_t, size_t, int, int) const; }; // ------------------------------------------------------------ Jeu::Jeu(size_t taille) : grille(taille, vector<Couleur>(taille, vide)) {} // ------------------------------------------------------------ bool Jeu::jouer(size_t i, Couleur c) { if (c == vide) return false; if (i >= get_taille()) return false; size_t j(0); while ((j < get_taille()) and (grille[i][j] != vide)) ++j; if (j >= get_taille()) return false; grille[i][j] = c; return true; } // ------------------------------------------------------------ Couleur Jeu::gagnant() const { Couleur vainqueur(vide); for (size_t i(0); i < get_taille(); ++i) { for (size_t j(0); j < get_taille(); ++j) { if (grille[i][j] != vide) { vainqueur = grille[i][j]; // teste dans les 5 directions possibles (+ (0,0) qui ne fait // rien) for (int di(0); di <= 1; ++di) for (int dj(-1); dj <= 1; ++dj) if (compte(vainqueur, i, j, di, dj) >= 4) return vainqueur; } } } return vide; } // ------------------------------------------------------------ unsigned int Jeu::compte(Couleur couleur, size_t i, size_t j, int di, int dj) const { unsigned int n(1); if ((di != 0) or (dj != 0)) { for (i += di, j += dj; (i < get_taille()) and (j < get_taille()) and (grille[i][j] == couleur); i += di, j += dj) ++n; } return n; } // ------------------------------------------------------------ bool Jeu::fini(Couleur& resultat) const { resultat = gagnant(); if (resultat == vide) { // est-ce plein ? for (auto ligne : grille) { for (auto kase : ligne) { // "case" est un mot réservé du C++ ;-) if (kase == vide) return false; } } } return true; } // ------------------------------------------------------------ ostream& operator<<(ostream& out, const Jeu& jeu) { return jeu.affiche(out); } ostream& Jeu::affiche(ostream& out) const { for (auto ligne : grille) { for (auto kase : ligne) { switch (kase) { case vide : out << ' '; break; case rouge : out << '#'; break; case jaune : out << 'O'; break; } } out << endl; } for (size_t i(0); i < get_taille(); ++i) out << '-'; out << endl; for (size_t i(1); i <= get_taille(); ++i) out << i; out << endl; return out; } // ==================================================================== class Joueur { public: Joueur(string nom, Couleur couleur = rouge) : nom(nom), couleur(couleur) {} virtual ~Joueur() {} virtual void jouer(Jeu&) const = 0; string get_nom() const { return nom; } Couleur get_couleur() const { return couleur; } protected: string nom; Couleur couleur; }; // ==================================================================== class Partie { public: Partie(Joueur const*, Joueur const*); void lancer(); void vider(); protected: array<Joueur const*, 2> joueurs; Jeu jeu; }; // ------------------------------------------------------------ Partie::Partie(Joueur const* j1, Joueur const* j2) { joueurs[0] = j1; joueurs[1] = j2; } // ------------------------------------------------------------ void Partie::lancer() { unsigned int tour(0); Couleur vainqueur; do { joueurs[tour]->jouer(jeu); tour = 1 - tour; // joueur suivant : 0 -> 1 1 -> 0 } while (not jeu.fini(vainqueur)); cout << "La partie est finie." << endl; cout << jeu << endl; if (vainqueur == joueurs[0]->get_couleur()) { cout << "Le vainqueur est " << joueurs[0]->get_nom() << endl; } else if (vainqueur == joueurs[1]->get_couleur()) { cout << "Le vainqueur est " << joueurs[1]->get_nom() << endl; } else { cout << "Match nul." << endl; } } // ------------------------------------------------------------ void Partie::vider() { for (auto joueur : joueurs) delete joueur; } // ==================================================================== class Humain : public Joueur { public: Humain(Couleur couleur = rouge) : Joueur("quidam", couleur) { cout << "Entrez votre nom : " << flush; cin >> nom; } void jouer(Jeu&) const; }; // ------------------------------------------------------------ void Humain::jouer(Jeu& jeu) const { cout << jeu << endl; size_t lu; bool valide; do { cout << "Joueur " << nom << ", entrez un numéro de colonne" << endl << " (entre 1 et " << jeu.get_taille() << ") : "; cin >> lu; // on pourrait faire ici la validation de la lecture --lu; // remet entre 0 et taille-1 (indice à la C++) valide = jeu.jouer(lu, couleur); if (not valide) cout << "-> Coup NON valide." << endl; } while (not valide); } // ==================================================================== class Ordi : public Joueur { public: Ordi(Couleur couleur = jaune) : Joueur("Le programme", couleur) {} void jouer(Jeu&) const; }; // ------------------------------------------------------------ void Ordi::jouer(Jeu& jeu) const { for (size_t i(0); i < jeu.get_taille(); ++i) { if (jeu.jouer(i, couleur)) { cout << nom << " a joué en " << i+1 << endl; return; } } } // ==================================================================== int main() { Partie p(new Ordi(rouge), new Humain(jaune)); p.lancer(); p.vider(); return 0; }