Choisissez votre style : style 1,    style 2,    sans commentaire,    impression,    ancien.

Correction
Polymorphisme (1/2)

Exercice 1 Exercice 2

Exercice 1 : formes polymorphiques

Exercice n°60 (pages 150 et 335) de l'ouvrage C++ par la pratique.

1.1 Formes :

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;
}
 

1.2 Formes abstraites

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

Exercice 2 : Puissance 4

Exercice n°61 (pages 151 et 342) de l'ouvrage C++ par la pratique.
#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;
}
 

Dernière mise à jour : Dernière mise à jour le 25 mars 2014
Last modified: Tue Mar 25, 2014