Solutions du TD2 POO

Table des matières

1 Section 1 Héritage

1.1 Question 1

Voiçi le code complet du fichier PersonnageDeJeu.h qui répond à la description UML de la question 1. La classe PersonnageDeJeu dérive de la classe Personnage. Cela s'écrit en C++: class PersonnageDeJeu : public Personnage. Ne pas oublier d'inclure le fichier Personnage.h auparavant. Le reste de la spécification est similaire à la spécification des constructeurs, des méthodes et des attributs de la classe Personnage.

#ifndef __PERSONNAGE_H
#define __PERSONNAGE_H
#include<string>
#include"Personnage.h"

class PersonnageDeJeu : public Personnage
{
private:
  unsigned int _pointsDeVie;

public:
  PersonnageDeJeu();
  PersonnageDeJeu(string nom, 
                  string prenom, 
                  int age, 
                  Genre genre, 
                  unsigned int points);
  void incremente(unsigned int points);
  void decremente(unsigned int points);
  unsigned int points();
  void changePoints(unigned points);
};
#endif

Voiçi le code complet du fichier PersonnageDeJeu.cpp. La mise en œuvre des méthodes est totalement similaire à la mise en œuvre des méthodes de la classe Personnage. La différence est au niveau de la mise en œuvre des constructeurs. La classe PersonnageDeJeu hérite de la classe Personnage, donc le constructeur de PersonnageDeJeu doit appeler le constructeur de Personnage pour initialiser la partie Personnage de l'objet PersonnageDeJeu (initialisation de tous les attributs venant de Personnage) puis initialiser les attributs de PersonnageDeJeu qui lui sont propres (attributs qui n'appartiennent pas à la classe Personnage), c'est à dire ici l'attribut _pointsDeVie. Pour le constructeur par défaut, on appelle le constructeur par défaut de Personnage (qui doit mettre les noms, prénoms à vide, age à zero, genre Feminin, si ce n'est pas le cas dans votre classe Personnage modidiez son constructeur en conséquence). Pour le constructeur paramétré, on appelle le constructeur paramétré de la classe Personnage.

PersonnageDeJeu::PersonnageDeJeu():Personnage(),
                                   _pointsDeVie(0)
{}

PersonnageDeJeu::PersonnageDeJeu(string nom, 
                                 string prenom, 
                                 int age, 
                                 Genre genre, 
                                 unsigned int points):
     Personnage(nom,prenom,age,genre),
     _pointsDeVie(points)
{}

void PersonnageDeJeu::incremente(unsigned int points)
{
    _pointsDeVie+=points;
}

void PersonnageDeJeu::decremente(unsigned int points)
{
   _pointsDeVie-=points;
}

unsigned int PersonnageDeJeu::points()
{
   return _points; 
}

void PersonnageDeJeu::changePoints(unigned points)
{
   _pointsDeVie  = points;
}

1.1.1 Combien de méthodes dans la classe Personnage ?

La classe Personnage contient 11 méthodes selon le diagramme UML (et 4 attributs).

1.1.2 Combien de méthodes dans la classe PersonnageDeJeu ?

La classe PersonnageDeJeu contient 4 méthodes qui lui sont propres mais dispose également de toutes les méthodes publiques et protégées de Personnage, autrement dit la classe PersonnageDeJeu dispose de 15 méthodes. Un objet de type PersonnageDeJeu est constitué de 5 attributs.

1.2 Question 2

Voiçi le programme principal complet répondant à la question 2. Ici, je fais une fonction intermédiaire test_personnageDeJeu() que j'appelle dans la fonction main(). Pas de difficulté particulière. Une classe qui hérite d'une autre classe s'emploie de la même façon qu'une classe simple.

#include<iostream>
#include"PersonnageDeJeu.h"

using namespace std;
// definition de la fonction test_personnageDeJeu() avant le main()

void test_personnageDeJeu()
{
  PersonnageDeJeu frodon("Saquet","Frodon",65,Genre::MASCULIN,30);
  cout << "Nom: " << frodon.nom() << endl;
  cout << "Prénom: " << frodon.prenom() << endl;
  if(frodon.genre()==Genre::MASCULIN)
  {
     cout << "Genre: MASCULIN" << endl;
  }
  else
  {
     cout << "Genre: FEMININ" << endl;
  }
  cout << "Age: " << frodon.age() << endl;
  cout << "Points de vie: " << frodon.points() << endl;
  frodon.decremente(3);
  cout << "Age: " << frodon.points() << endl;
  frodon.incremente(6);
  cout << "Age: " << frodon.points() << endl;
  frodon.changePrenom("Galadriel");
  frodon.changeNom("Dame de Lorien");
  frodon.changeGenre(Genre::FEMININ);
  frodon.changeAge(1040);
  frodon.changePoints(30000);
  cout << "Nom: " << frodon.nom() << endl;
  cout << "Prénom: " << frodon.prenom() << endl;
  if(frodon.genre()==Genre::MASCULIN)
  {
     cout << "Genre: MASCULIN" << endl;
  }
  else
  {
     cout << "Genre: FEMININ" << endl;
  }
  cout << "Age: " << frodon.age() << endl;
  cout << "Points de vie: " << frodon.points() << endl;
}

int main()
{
  test_personnageDeJeu();
  return 0;
}


1.3 Question 3

Voiçi le fichier de spécification de la classe Chevalier. Cette classe dérive de la classe PersonnageDeJeu. En écrivant class Chevalier : public PersonnageDeJeu on met en œuvre tout l'arbre UML de l'héritage car PersonnageDeJeu hérite de Personnage. Il n'est pas utile de préciser que Chevalier hérite de Personnage, c'est implicite. La nouveauté dans cette classe Chevalier est que l'on veut remplacer la définition de la méthode identite() issue de la classe Personnage par une définition qui est propre à tout Chevalier. Pour faire ce remplacement, il faut respecifier la méthode identite() dans Chevalier. De plus, il faut dire au compilateur que cette méthode devra remplacer l'autre (de la classe Personnage), en ajoutant le mot clé virtual. Enfin, à cause de l'apparition de méthodes virtuelles, il faudra explicitement déclarer le destructeur et dire qu'il est lui-même virtuel virtual ~Chevalier()

#ifndef __CHEVALIER_H
#define __CHEVALIER_H
#include "PersonnageDeJeu.h"

class Chevalier: public PersonnageDeJeu
{
  public:
    Chevalier();
    Chevalier(string nom, string prenom, unsigned int age, Genre genre, unsigned int points);
    virtual ~Chevalier();  // desctructeur virtuel
    virtual string identite(); // methode virtuelle, elle peut remplacer ou être remplacée dans la chaine d'heritage
};

#endif

La mise en œuvre de la classe Chevalier est la suivante. Les constructeurs de Chevalier appellent les constructeurs respectifs de PersonnageDeJeu (qui, quant à eux, appelleront les constructeurs de la classe Personnage). La classe Chevalier n'a pas d'attributs qui lui est propre donc les constructeurs de Chevalier n'ont rien d'autres à faire. Le destructeur de Chevalier ne fait rien mais il faut le définir parce que l'on l'a spécifié (destructeur virtuel). La méthode identite() est donc redéfinie ici mais notez que l'on a employé la méthode prenom() de la classe Personnage et non pas son attribut _prenom. En effet, _prenom est un attribut privé de la classe Personnage et donc la classe Chevalier n'y a pas accès, il faut utiliser la méthode publique prenom() pour accéder au prénom du chevalier.

Chevalier()::Chevalier():PersonnageDeJeu()
{}

Chevalier()::Chevalier(string nom, 
                       string prenom, 
                       unsigned int age, 
                       Genre genre, 
                       unsigned int points):
             PersonnageDeJeu(nom,prenom,age,genre,points)
{}


Chevalier::~Chevalier(){}


string Chevalier::identite()
{
   cout << "Moi, preux chevalier " << prenom() << ", je suis là pour vous servir." << endl;
}

La mise en œuvre n'est pas terminée. La méthode identite() étant redéfinie dans Chevalier, elle doit donc être déclarée virtual dans Personnage. Et comme Personnage contient désormais une méthode virtuelle, il faut spécifier et définir un destructeur virtual sur toute la chaine d'héritage.

Dans Personnage.h, on ajoute donc virtual devant la méthode identite() et le destructeur.

virtual ~Personnage();
virtual string identite();

Dans Personnage.cpp, si le destructeur n'est pas déjà défini, il faut le faire.

Personnage::~Personnage(){}

Dans PersonnageDeJeu.h, on ajoute donc le destructeur virtuel.

virtual ~PersonnageDeJeu();

Dans PersonnageDeJeu.cpp, on le définit.

PersonnageDeJeu::~PersonnageDeJeu(){}

1.3.1 En quoi la méthode identite() de la classe Chevalier spécialise la méthode identite() de Personnage ?

La méthode identite() de la classe Chevalier redéfinit le comportement de la méthode identite() de Personnage. Autrement dit si l'objet qu'on manipule est un Chevalier, l'appel de la méthode identite() correspondra à la définition donnée dans la classe Chevalier. Mais Chevalier est un Personnage (il en hérite) mais c'est un Personnage spécifique. Tout objet qui hérite de Personnage (un objet de type Personnage ou PersonnageDeJeu) et qui n'est pas un Chevalier exploitera la version générale de identite(). C'est en ce sens que l'on dit que la méthode identite() de la classe Chevalier spécialise celle de Personnage, son emploi étant restreint à un sous ensemble de Personnage.

1.4 Question 4

Je fais une fonction test_Polymorphisme() dans le main.cpp. À travers cette fonction s'exprime une grande partie du potentiel de la programmation orientée objet. De part l'héritage, un Chevalier est un Personnage, un PersonnageDeJeu est un Personnage. Aussi, on peut stocker dans un pointeur de Personnage l'adresse de tout objet dont la classe dérive de Personnage. Donc le vecteur vector<Personnage *> peut stocker des pointeurs sur des objets de classe Personnage, PersonnageDeJeu et Chevalier. Le polymorphisme s'exprime ici au moment de l'appel à la méthode identite(). Quand l'objet pointé est un Personnage quelconque (comme Frodon ici), la version de identite() qui est employée est celle de Personnage. Mais quand l'objet en question est un Chevalier (Boromir ici), c'est la version spécialisée de Chevalier qui est employé. La méthode identite() a plus plusieurs formes, elle est polymorphe. Et dans le code de l'itération, l'emploi de la méthode identite() n'est pas distingué.

void test_Polymorphisme()
{
  vector<Personnage *> personnages;
  personnages.push_back(new PersonnageDeJeu("Saquet","Frodon",65,Genre::MASCULIN,30);
  personnages.push_back(new Chevalier("du Gondor","Boromir",41,Genre::MASCULIN,2);
  for(size_t i = 0; i < personnages.size(); ++i)
  {
    cout << personnages.at(i)->identite() << endl;
  }
  // on détruit les objets, ne pas oublier
   for(size_t i = 0; i < personnages.size(); ++i)
  {
     delete personnages.at(i);
  }
}

int main()
{
   test_Polymorphisme();
   return 0;
} 

Enfin, ne pas oublier de détruire les objets en fin de fonction.

1.5 Question 5

Dans Chevalier.h, on rajoute:

virtual string identite(string interlocuteur);

Dans Chevalier.cpp, on rajoute:

string Chevalier::identite(string interlocuteur)
{
  cout << "Moi, preux chevalier " 
       << prenom() 
       << " je suis là pour vour servir " 
       << interlocuteur << "." << endl;
}

Dans le main()

#include <iostream>
#include "Chevalier.h"
int main()
{
  Chevalier lancelot("Du Lac","Lancelot",23, Genre::MASCULIN, 40);
  cout << lancelot.identite("Belle Guenièvre");
}

Ici la méthode identite(string interlocuteur) est différent de la méthode identite() du Chevalier car elle a une liste de paramètre différente. C'est en cela que c'est une surcharge et non pas une spécialisation.

1.6 Question 6

Comme demandé, on va tout d'abord passer l'attribut _pointsDeVie de Personnage en mode protégé pour que toutes ses classes dérivées (Chevalier et Magicien) puissent directement exploiter cet attribut. De même la méthode incremente() devient virtuelle.

Dans PersonnageDeJeu, on modifie:

class PersonnageDeJeu: public Personnage
{
protected: // acces protege
  unsigned int _pointsDeVie;


public:
  virtual void incremente(unsigned int points);

};

Voici la spécification de la classe Magicien (très proche de Chevalier), mais on spécialise incremente cette fois-ci.

#ifndef __MAGICIEN_H
#define __MAGICIEN_H
#include "PersonnageDeJeu.h"

class : public PersonnageDeJeu
{
  public:
    Magicien();
    Magicien(string nom, string prenom, unsigned int age, Genre genre, unsigned int points);
    virtual ~Magicien();  // desctructeur virtuel
    virtual void incremente(unsigned int points); // methode virtuelle, elle peut remplacer ou être remplacée dans la chaine d'heritage
};

#endif

Voiçi sa définition.

Magicien::Magicien():PersonnageDeJeu()
{}

Magicien::Magicien(string nom, 
                       string prenom, 
                       unsigned int age, 
                       Genre genre, 
                       unsigned int points):
             PersonnageDeJeu(nom,prenom,age,genre,points)
{}


Magicien::~Magicien(){}


void Magicien::incremente(unsigned int points)
{
   _pointsDeVie += 2*points; // on a le droit de faire cela car _pointsDeVie est en mode protege
   // en mode prive, cela n'aurait pas été possible
}

Un exemple pour tester le polymorphisme de la méthode incremente.

void test_Magicien()
{
  vector<Personnage *> personnages;
  personnages.push_back(new PersonnageDeJeu("Saquet","Frodon",65,Genre::MASCULIN,30);
  personnages.push_back(new Chevalier("du Gondor","Boromir",41,Genre::MASCULIN,2);
  personnages.push_back(new Magicien("Le Gris","Gandalf",2000,Genre::MASCULIN,20000);
  for(size_t i = 0; i < personnages.size(); ++i)
  {
    cout << personnages.at(i)->prenom() 
         << " a " 
         << personnages.at(i)->points() 
         << " points de vie" << endl;
    personnages.at(i)->incremente(10);
    cout << personnages.at(i)->prenom() 
         << " a " 
         << personnages.at(i)->points() 
         << " points de vie" << endl;

  }
  // on détruit les objets, ne pas oublier
   for(size_t i = 0; i < personnages.size(); ++i)
  {
     delete personnages.at(i);
  }
}

int main()
{
   test_Magicien();
   return 0;
} 

2 Section 2 Association/Agrégation/Composition

2.1 Question 1

On commence par un début de spécification/définition de Cheval dans les fichiers Cheval.h et Cheval.cpp

1- Spécification

class Cheval
{
private:
   string _nom;
public:
   Cheval(string nom);
   string nom();
};

2- Définition

#include "Cheval.h"


Cheval::Cheval(string nom): _noom(nom){}

string Cheval::nom() 
{
  return _nom;
}

3- Mise en œuvre de l'association destrier

Un Chevalier peut avoir au plus un destrier, il peut également en changer. On met en œuvre le lien entre le Chevalier et le Cheval par un attribut de type Cheval * _destrier dans la classe Chevalier et on y ajoute les accesseurs/mutateurs nécessaires. On ajoute également une méthode qui indique si le Chevalier a un destrier.

Dans Chevalier.h

#include "Cheval.h"

class Chevalier: public PersonnageDeJeu
{
private:
  Cheval * _destrier;
public:
  Cheval * destrier();
  void changeDestrier(Cheval * nouveauDestrier);
  bool aUnDestrier();
};

Dans Chevalier.cpp, on met en œuvre les méthodes en question. On met également à jour les constructeurs pour gérer l'attribut _destrier qui vaudra nullptr si le Chevalier n'a pas de destrier. On peut également mettre en place un nouveau constructeur pour initialiser un destrier mais ce n'est pas nécessaire. La spécification UML est bien respectée comme cela.

#include "Chevalier.h"

Chevalier()::Chevalier():PersonnageDeJeu(),_destrier(nullptr)
{}

Chevalier()::Chevalier(string nom, 
                       string prenom, 
                       unsigned int age, 
                       Genre genre, 
                       unsigned int points):
             PersonnageDeJeu(nom,prenom,age,genre,points),
             _destrier(nullptr)
{}

Cheval * Chevalier::destrier()
{
   return _destrier;
}

void Chevalier::changeDestrier(Cheval * nouveauDestrier)
{
  _destrier = nouveauDestrier;
}

bool Chevalier::aUnDestrier()
{
  return _destrier != nullptr;
}


3- Mise en œuvre de l'association maitre

Un Cheval peut avoir au plus un maitre, il peut également en changer. On met en œuvre le lien entre le Cheval et le Chevalier par un attribut de type Chevalier * _maitre dans la classe Cheval et on y ajoute les accesseurs/mutateurs nécessaires. On ajoute également une méthode qui indique si le Cheval a un maitre.

#include<string>
using namespace std;

class Chevalier; // prédéclaration de Chevalier, car on ne peut pas inclure Chevalier.h

class Cheval
{
private:
   string _nom;
   Chevalier * _maitre;
public:
   Cheval(string nom);
   string nom();
   Chevalier * maitre();
   void changeMaitre(Chevalier * nouveauMaitre);
   bool aUnMaitre();
};

Attention, dans ce qui précède, on n'a pas inclus le fichier Chevalier.h (pas de #include"Chevalier.h"). La classe Cheval a besoin de connaître la classe Chevalier donc on aurait dû inclure Chevalier.h. Seulement, il y a un hic: le fichier Chevalier.h inclut également le fichier Cheval.h. Si Chevalier.h inclut Cheval.h et que Cheval.h inclut Chevalier.h, on ne s'en sort pas : cycle de dépendances!!. Pour casser ce cycle, on prédéclare la classe Chevalier dans Cheval.h sans inclure Chevalier.h. Cela va suffir. On va inclure Chevalier.h uniquement dans Cheval.cpp.

#include "Cheval.h"
#include "Chevalier.h"

Cheval::Cheval(string nom): _nom(nom),_maitre(nullptr){}

Chevalier * Cheval::maitre()
{
   return _maitre;
}

void Cheval::changeMaitre(Chevalier * nouveauMaitre)
{
  _maitre = nouveauMaitre;
}

bool Cheval::aUnMaitre()
{
  return _maitre != nullptr;
}


4- Un petit exemple de manipulation de cette association à insérer dans main.cpp

#include"Cheval.h"
#include"Chevalier.h"

void test_Association
{
    Cheval hasufel("Hasufel");
    Chevalier aragorn("du Gondor","Aragorn",49,Genre::MASCULIN,40);
    if(aragorn.aUnDestrier())
    {
        cout << aragorn.prenom() << " a un destrier.\n";
    }
    else
    {
       cout << aragorn.prenom() << " n'a pas de destrier.\n";
    } 
    if(hasufel.aUnMaitre())
    {
        cout << hasufel.nom() << " a un maitre.\n";
    }
    else
    {
       cout << hasufel.nom() << " n'a pas de maitre.\n";
    }
    aragorn.changeDestrier(hasufel);
    hasufel.changeMaitre(aragorn);
    cout << "Le maitre de " << hasufel.nom() << " est " << hasufel.maitre()->prenom() << '\n';
    cout << "Le cheval de " << aragorn.prenom() << " est " << aragorn.destrier()->nom() << '\n';
    aragorn.changeDestrier(nullptr);
    hasufel.changeMaitre(nullptr);
    cout << hasufel.nom() << " n'a plus de maitre\n";
    cout << aragorn.prenom() << "n'a plus de destrier\n";
}

2.2 Question 2

On commence par mettre en œuvre la classe Sort dans Sort.h et Sort.cpp. Rien de particulier ici, très simple.

1- Sort.h

#include <string>

using namespace std;

class Sort
{
private:
  string _intitule;
public:
  Sort(string nom);
  string intitule();
};

2- Sort.cpp

#include "Sort.h"

Sort::Sort(string nom):_intitule(nom){}

string Sort::intitule()
{
   return _intitule;
}

3- Mise en œuvre de l'aggrégation.

L'idée est qu'un magicien stocke les sorts qu'il connait. On va donc lui ajouter un attribut vector<Sort *>. On met en place un moyen d'ajouter des sorts ajouterSort(Sort *). On rajoute d'autres méthodes pour faciliter la gestion des sorts.

Dans Magicien.h

#include <vector>
#include "Sort.h"

using namespace std;

class Magicien
{
  private:
    vector<Sort *> _sorts;
  public:
    Sort * sort(unsigned int index);
    unsigned int nombreDeSorts();
    void ajouteSort(Sort * sort);

};

Dans Magicien.cpp, on met en œuvre les méthodes spécifiées, on met à jour les constructeurs.

#include"Magicien.h"

Magicien::Magicien():PersonnageDeJeu(),_sorts()
{}

Magicien::Magicien(string nom, 
                       string prenom, 
                       unsigned int age, 
                       Genre genre, 
                       unsigned int points):
             PersonnageDeJeu(nom,prenom,age,genre,points),
             _sorts()
{}

Sort * Magicien::sort(unsigned int index)
{
  return _sorts.at(index);
}
unsigned int Magicien::nombreDeSorts()
{
  return _sorts.size();
}
void Magicien::ajouteSort(Sort * sort)
{
  _sorts.push_back(sort);
}


3- Exemple d'utilisation de l'aggrégation

void test_Agregation()
{

  Magicien * gandalf = new Magicien("Le Gris","Gandalf",2000,Genre::MASCULIN,20000);
  Sort * rayonDeLumiere = new Sort("Rayon de Lumière");
  Sort * telepathie = new Sort("Télépathie");
  Sort * persuasion = new Sort("Persuasion");

  gandalf->ajouteSort(rayonDeLumiere);
  gandalf->ajouteSort(telepathie);
  gandalf->ajouteSort(persuasion);

  for(size_t i = 0; i < gandalf->nombreDeSorts(); ++i)
  {
      cout << gandalf->sort(i) << "\n"
  }
  //
  delete persuasion;
  delete telepathie;
  delete rayonDeLumiere;
  delete gandalf;
}

int main()
{
   test_Agregation();
   return 0;
} 

2.3 Question 3

On commence par la mise en œuvre de la barbe du nain. Aucune difficulté.

Barbe.h:

class Barbe
{
private:
  unsigned int _longueur;

public:
  Barbe();
  unsigned int longueur();
  int changeLongueur();
  // destructeur par défaut, C++ le construira pour moi
};

Barbe.cpp:

#include"Barbe.h"

Barbe::Barbe():_longueur(0){}


unsigned int Barbe::longueur()
{
  return _longueur;
}

int Barbe::changeLongueur(unsigned int longueur)
{
  _longueur = longueur;
}

Maintenant, on créé Nain. Un Nain dérive de la classe PersonnageDeJeu et il est composé d'une Barbe. La Barbe est propre au Nain et devra être détruite quand le Nain sera détruit (contrairement au Sort de Magicien). On va donc stocker un objet Barbe dans Nain. La méthode changeAge de Nain est une spécialisation de celle de Personnage, il faut la virtualiser.

Nain.h

#include"PersonnageDeJeu.h"
#include"Barbe.h"

class Nain : public PersonnageDeJeu
{
  private:
    Barbe _barbe;

  public:
    Nain();
    Nain(string nom, string prenom, unsigned int age, Genre genre, unsigned int points);
    virtual ~Nain();  // desctructeur virtuel
    unsigned int longueurBarbe();
    virtual void changeAge(unsigned int age);
};

Nain.cpp

#include "Nain.h"
Nain::Nain():PersonnageDeJeu(),_barbe(){}

Nain::Nain(string nom, string prenom, unsigned int age, Genre genre, unsigned int points):
PersonnageDeJeu(nom,prenom,age,genre,points),_barbe(){}

Nain::~Nain(){}  // desctructeur virtuel


unsigned int Nain::longueurBarbe()
{
  return _barbe.longueur();
}

void Nain::changeAge(unsigned int age)
{
  Personnage::changeAge(age); // on appelle la version Personnage de changeAge
  _barbe.changeLongueur(10 + age()/10);
}

Ne pas oublier de virtualiser la méthode changeAge de Personnage !!

class Personnage
{
  public:
    virtual void changeAge(unsigned int age);

};

Petit Exemple d'utilisation dans le main.cpp


void testNain()
{
  Nain * maitreGim = new Nain("Fils de Glóin","Gimli",39,Genre::MASCULIN,30);
  cout << "La longueur de la barbe de " << maitreGim->prenom() 
       << " est " <<  maitreGim->longueurBarbe() << endl;
  // les années passent
  maitreGim->changeAge(60);
  cout << "La longueur de la barbe devient " 
       << maitreGim->longueurBarbe() << endl;
  delete maitreGim;
}


2.4 Question 4

Cette association met en lien des objets de la même classe. Un Magicien a au plus un maître et deux élèves Magicien. Pour l'association maitre, on va utiliser un pointeur Magicien * _maitre. Pour l'association eleve, on va utiliser un vecteur de deux pointeurs et s'assurer qu'il est toujours de taille 2. On va mettre en œuvre une gestion adaptée. Ce n'est qu'une solution, pas la seule.

Dans Magicien.h

class Magicien  : public PersonnageDeJeu
{
private: 
   Magicien * _maitre;
   vector<Magicien *> _eleves;

public:
   void changeMaitre(Magicien * nouveauMaitre);
   Magicien * maitre();
   bool aUnMaitre();
   void changeEleve(Magicien * nouvelEleve, unsigned int index);
   Magicien * eleve(unsigned int index);
   bool aEleve(unsigned int index);
   unsigned int nombreEleves();

};

Dans Magicien.cpp, on met à jour les constructeurs et on met en œuvre le reste

 Magicien::Magicien():
     PersonnageDeJeu(),
     _sorts(),
     _maitre(nullptr), // pas de maitre
     _eleves(2) // creation d'un vecteur de 2 cases
     {
        _eleves.at(0) = nullptr; // pas d'eleve
        _eleves.at(1) = nullptr;
     }

Magicien::Magicien(string nom,
                   string prenom, 
                   unsigned int age, 
                   Genre genre, 
                   unsigned int points):
                   PersonnageDeJeu(nom,prenom,age,genre,points),
                   _sorts(),
                   _maitre(nullptr), // pas de maitre
                   _eleves(2) // creation d'un vecteur de 2 cases
 {
 _eleves.at(0) = nullptr; // pas d'eleve
 _eleves.at(1) = nullptr;
 }

 void Magicien::changeMaitre(Magicien * nouveauMaitre)
 {
   _maitre = nouveauMaitre;
 }

 Magicien * Magicien::maitre()
 {  
   return _maitre;
 }
 bool Magicien::aUnMaitre()
 {
   return _maitre != nullptr;
 }
 void Magicien::changeEleve(Magicien * nouvelEleve, unsigned int index)
 {
   _eleves.at(index) = nouvelEleve; 
 }
 Magicien * Magicien::eleve(unsigned int index)
 {
   return _eleves.at(index);
 }
 bool Magicien::aEleve(unsigned int index)
 {
   return _eleves.at(index) != nullptr;
 }
 unsigned int Magicien::nombreEleves()
 {
    unsigned int counter = 0;
    if(aEleve(0))
    {
       ++counter;
    }
    if(aEleve(1))
    {
       ++counter;
    }
    return counter;
 } 

Une utilisation de cette association

void testMagiciensEleves()
{
   Magicien * merlin = new Magicien("L'enchanteur","Merlin",, 60, Genre::MASCULIN, 100);
   Magicien * viviane = new Magicien("Dame Du Lac","Viviane", 60, Genre::FEMININ, 100);
   Magicien * blaise =  new Magicien("Le Scribe","Blaise", 60, Genre::MASCULIN, 20);
   cout << "Le nombre d'élèves de " 
        << merlin->prenom() << " est " 
        << merlin->nombreEleves() 
        << endl;
   merlin->changeEleve(viviane,0);
   merlin->changeEleve(blaise,1);
   viviane->changeMaitre(merlin);
   blaise->changeMaitre(merlin);
   cout << "Le nombre d'élèves de " 
        << merlin->prenom() << " est " 
        << merlin->nombreEleves() 
        << endl;
   cout << "Le maitre de " << viviane->prenom() << " est " << viviane->maitre()->prenom() << "endl";

   delete blaise;
   delete viviane;
   delete merlin;
}

2.5 Question 5

td2uml.png

Auteur: Yannick Pencolé (yannick.pencole@laas.fr)

Created: 2023-02-19 dim. 00:49

Validate