Solutions du TD1 POO

Table des matières

1. Section 3 Ma première classe

1.1. Question 3.3

Déclaration de la classe Personnage. Cette déclaration est à include dans le fichier Personnage.h. Normalement, Netbeans a déjà créé automatiquement les macros pour la non-double inclusion des fichiers de spécification (c.à.d #ifndef... #define ... #endif). Les attributs de la classe sont à accès privé, donc ils sont déclarés dans un bloc qui débute par le mot clé private. Deux attributs sont de classe std::string, on doit donc inclure le fichier #include<string>. Afin d'éviter d'écrire std::string à chaque fois, on demande au compilateur d'aller systématiquement regarder le nom des fonctions/classes de la bibliothèque standard (std) pour compiler avec l'instruction using namespace std; (std est l'espace de nommage de la bibliothèque C++). Ainsi on peut se contenter d'écrire string au lieu de std::string.

#ifndef PERSONNAGE_H
#define PERSONNAGE_H
#include<string>

using namespace std;


class Personnage
{
private:
string _nom;
string _prenom;
unsigned _age;


};
#endif

1.2. Question 3.4

Le fichier Personnage.cpp est le fichier qui contiendra la mise en oeuvre des constructeurs et des méthodes de la classe Personnage. Il démarrera par l'inclusion du fichier de spécification Personnage.h

#include"Personnage.h"

1.3. Question 3.5

Il faut d'abord spécifier le constructeur dans la classe Personnage dans le fichier Personnage.h. Le constructeur est à accès public, il doit donc être déclaré dans un bloc qui démarre avec le mot-clé public. Tout constructeur porte le nom de la classe (donc ici c'est Personnage) et le constructeur par défaut n'aucun paramètre. Notez qu'un constructeur n'a pas de type de retour.

...

class Personnage
{
...
public: 
Personnage();


...
};
...

Ensuite on le met en oeuvre dans le fichier Personnage.cpp. L'initialisation des attributs doit respecter l'ordre des attributs qui ont été déclaré dans Personnage.h. Donc ici on initialise d'abord _nom puis _prenom puis _age. Dans le fichier Personnage.cpp, il faut nécessairement faire précéder le nom des méthodes/constructeurs du préfixe Personnage::.

Personnage::Personnage():_nom("Doe"),_prenom("John"),_age(20){}

1.4. Question 3.6

Dans cette question on rajoute un message dans le corps du constructeur par défaut dans le fichier Personnage.cpp. À chaque fois que l'on appelle ce constructeur, le message s'affichera à l'écran, pratique pour comprendre ce qui se passe. Pour afficher à l'écran on utilise des opérateurs de flux <<. La sortie standard d'un programme (qui est l'écran) est appelée cout et est définie dans le fichier d'entête iostream de la bibliothèque standard.

#include<iostream>
...

using namespace std;

Personnage::Personnage():_nom("Doe"),_prenom("John"),_age(20)
{
   cout << "Le personnage " << _prenom << " " << _nom
        << " est né et il a déjà " << _age << " ans.\n";
}

1.5. Question 3.7

Le destructeur est unique et il a toujours la même forme: le nom de la classe précédé d'un tilde ~

On le déclare dans le fichier Personnage.h

class Personnage
{
...
public:
... 
~Personnage();
...


};

Et on le met en œuvre dans Personnage.cpp. Dans cet exercice, on demande à ce que le destructeur affiche un message. C'est juste pour faire comprendre quand le programme utilise le destructeur d'un objet. En pratique, ce destructeur n'affiche jamais de message. On peut également ne pas le définir. Dans ce cas, le compilateur définira automatiquement un destructeur par défaut. La mise en œuvre du destructeur est obligatoire dès lors que la destruction de l'objet nécessite la destruction d'attributs alloués dynamiquement (il faut alors appeler delete explicitement sur ces attributs dans le corps du destructeur). Dans la classe Personnage, ce n'est pas le cas.

Personnage::~Personnage()
{
cout << "RIP " << _prenom << " " << _nom << '\n';
}

1.6. Question 3.8

On demande de créer un objet dans le programme principal (dans la fonction main()).

Par exemple:

#include "Personnage.h"

int main()
{
Personnage johnDoe;
return 0;
}

1.7. Question 3.9

Dans ce programme, le constructeur par défaut de la classe Personnage est appelé lorsque l'on déclare l'objet johnDoe. Au moment où la fonction main() exécute return 0, cette instruction fait se terminer la fonction main() qui commande la destruction automatique de l'objet johnDoe (appel du destructeur). L'objet johnDoe est détruit automatiquement car il a été alloué de façon statique ici (pas de new).

2. Section 4 Accesseurs et Modificateurs

2.1. Question 4.1

L'instruction demandée ne peut pas compiler. L'attribut _age est à accès privé dans la classe Personnage donc seule des méthodes/constructeurs/destructeurs de la classe Personnage y ont accès. Or on demande ici à la fonction main() d'y accéder, d'où l'erreur de compilation. Il nous définir un accesseur pour cet attribut.

2.2. Question 4.2

1- Déclaration de l'accesseur unsigned int age() dans Personnage.h. Il doit être en accès public:

class Personnage
{
...
public:
   unsigned int age();


};

2- Définition de l'accesseur dans Personnage.cpp.

L'accesseur est une fonction qui doit retourner la valeur de l'attribut privé _age de l'objet courant. Cette méthode appartient à la classe Personnage donc a effectivement accès à tous les attributs privé de l'objet courant.

unsigned int Personnage::age()
{
   return _age;
}

3- En remplaçant dans le main(), l'expression johnDoe._age par johnDoe.age(), cela compile. En effet age() est une méthode à accès publique, elle peut donc être utilisée dans toute fonction, et donc dans le main().

2.3. Question 4.3

Pour les mêmes raisons qu'en 4.1, cela ne compile pas. _age est à accès privé.

2.4. Question 4.4

1- Déclaration du mutateur à accès publique dans Personnage.h

class Personnage
{
...
public:
...
   void changeAge(unsigned int nouvelAge);


};

2- Définition du mutateur dans Personnage.cpp

void Personnage::changeAge(unsigned int nouvelAge)
{
   _age = nouvelAge;
}

3- Utilisation du mutateur dans le programme principal

#include"Personnage.h"

int main()
{
   Personnage johnDoe;
   johnDoe.changeAge(40);
   std::cout << "age du personnage: " << johnDoe.age() << '\n';
   return 0;
}

Le programme appelle la méthode publique changeAge sur l'objet johnDoe avec le paramètre 40. Donc la variable paramètre nouvelAge de la méthode changeAge a pour valeur 40 dans cet appel. Et l'attribut _age de l'objet johnDoe reçoit cette valeur lors de l'exécution de changeAge(40).

2.5. Question 4.5

1- Déclaration des mutateurs à accès publique dans Personnage.h

class Personnage
{
...
public:
...
   void changeNom(string nouveauNom);
   void changePrenom(string nouveauPrenom);

};

2- Définition des mutateurs dans Personnage.cpp

void Personnage::changeNom(string nouveauNom)
{
   _nom = nouvauNom;
}

void Personnage::changePrenom(string nouveauPrenom)
{
   _prenom = nouvauPrenom;
}

3. Section 5 Surcharge de constructeur

3.1. Question 5.1

1- On déclare le nouveau constructeur dans Personnage.h. On conserve le constructeur par défaut. On peut avoir autant de constructeur que l'on veut. Ils portent tous le même nom, qui est le nom de la classe. Ce qui va les différencier la liste de leur paramètres.

class Personnage
{
...
public:
   Personnage(); // constructeur par defaut
   Personnage(string prenom, string nom); // deuxième constructeur
...

}

2- Puis on rajoute sa définition dans Personnage.cpp

Personnage::Personnage(string prenom, string nom):_nom(nom),_prenom(prenom),_age(20)
{}

Ce constructeur initialise l'attribut _nom avec la valeur contenue dans le paramètre nom. Il fait de même pour _prenom. L'attribut _age est quant à lui initialisé à 20. L'ordre des initialisations est important, il doit être celui des déclarations des attributs dans la classe Personnage.

3- Dans le main(), on peut l'utiliser comme suit:

int main()
{
   Personnage ironMan("Tony","Stark");

}

3.2. Question 5.2

1- On déclare le nouveau constructeur dans Personnage.h. On conserve les deux autres constructeurs.

class Personnage
{
...
public:
   Personnage(); // constructeur par defaut
   Personnage(string prenom, string nom); // deuxième constructeur
   Personnage(string prenom, string nom, int age);

}

2- Puis on rajoute sa définition dans Personnage.cpp

Personnage::Personnage(string prenom, string nom, int age):_nom(nom),_prenom(prenom),_age(age)
{}

3- Dans le main(), on peut l'utiliser comme suit:

int main()
{
   Personnage ironMan("Tony","Stark",40);
}

3.3. Question 5.3

1- Le constructeur par copie est unique. On déclare le constructeur dans Personnage.h. On conserve les trois autres constructeurs.

class Personnage
{
...
public:
   Personnage(); // constructeur par defaut
   Personnage(string prenom, string nom); 
   Personnage(string prenom, string nom, int age); 
   Personnage(const Personnage & pers); // constructeur par copie

}

Ce constructeur a un seul paramètre particulier c'est une référence C++ (noté &) sur un objet pers de type Personnage qui est constant (const). Les références C++ sont difficiles à utiliser mais elles ressemblent à des pointeurs en moins flexibles, elles sont hors programme POO. Le mot-clé const est également hors programme, mais sachez qu'il est très important en pratique. Si vous souhaitez étendre vos connaissances en C++ après ce module POO, ces deux notions sont obligatoires à connaître.

2- Le constructeur par copie est défini dans Personnage.cpp

Personnage::Personnage(const Personnage & pers):_nom(pers._nom),_prenom(pers._prenom),_age(pers._age)
{}

Chaque attribut de l'objet courant est initialisé avec l'attribut respectif de l'objet pers. Notez que le constructeur Personnage faisant partie de la classe Personnage a accès aux attributs privés de l'objet courant mais aussi de l'objet pers.

3- Son utilisation dans le programme principal

int main()
{
 Personnage georgeEn1985("Georges","MacFly",47); // constructeur paramétré
 Personnage georgesEn1955(georgesEn1985); // constructeur par copie
 // 2 objets sont créés, le deuxième est une copie du premier
}

4. Section 6 Allocation dynamique d’objets Personnage

4.1. Question 6.1

L'allocation dynamique permet de créer un objet dans une fonction, objet qui est accédé par un pointeur et qui permet de pouvoir utiliser cet objet même si la fonction qu'il l'a créé s'est terminée. On dit qu'un objet alloué dynamiquement est persistent en mémoire (pas automatiquement détruit à la fin de la fonction). Mais du fait qu'il est persistent en mémoire, il faut nécessairement le détruire quand cet objet n'est plus utile.

Allocation dynamique d'un objet Personnage. L'objet est créé avec l'operateur new qui appelle un des constructeurs de la classe Personnage. L'opérateur new retourne l'adresse mémoire de l'objet ainsi créé, adresse qui est stockée dans un pointeur Personnage * dont le nom est ww.

int main()
{
Personnage * ww = new Personnage("Wonder","Woman", 230);


}

4.2. Question 6.2

Maintenant on change son nom et on l'affiche. Pour cela, on exploite la notation -> sur le pointeur ww.

int main()
{
Personnage * ww = new Personnage("Wonder","Woman", 230);
ww->changeNom("Man");
cout << ww->nom();

}

4.3. Question 6.3

L'objet doit être explicitement détruit à l'aide de delete, qui va appeler implicitement le destructeur de Personnage. Attention delete ne modifie pas la valeur contenue dans ww qui devient donc une adresse invalide car on vient de détruire l'objet qui y était. Le point ww est donc invalide. Pour exprimer que ww est invalide, il est préférable alors de le mettre à la valeur nullptr. Il est ainsi facile de tester plus tard que ww est invalide par un test d'égalité avec nullptr.

int main()
{
  Personnage * ww = new Personnage("Wonder","Woman", 230);
  ww->changeNom("Man");
  cout << ww->nom();
  delete ww;
  ww =nullptr;
  return 0;
}

5. Section 7 Enumérations

5.1. Question 7.1

Comme stipulé dans le sujet de TP, on peut définir un énumération "à la C" dans Personnage.h au dessus de la spécification de la classe Personnage.

enum Genre { MASCULIN, FEMININ };

Mais il est préférable en C++ d'utiliser une classe énumérée en insérant le mot-clé class.

enum class Genre { MASCULIN, FEMININ };

Dans la suite des questions on considère que Genre est une classe énumérée de C++.

5.2. Question 7.2

Dans la classe Personnage on rajoute l'attribut suivant.

class Personnage
{
  private:
   ...
   Genre _genre;
   ...
};

5.3. Question 7.3

On modifie les constructeurs.

1- modification des spécifications, on rajoute un paramètre Genre genre aux constructeurs paramétrés.

class Personnage
{
...
public:
   Personnage(); // constructeur par defaut
   Personnage(string prenom, string nom, Genre genre); 
   Personnage(string prenom, string nom, int age, Genre genre); 
   Personnage(const Personnage & pers); // constructeur par copie

};

2- modification des mises en œuvre, on doit initialiser l'attribut _genre dans tous les constructeurs

Personnage::Personnage():_nom("Doe"),
                         _prenom("John"),
                         _age(20),
                         _genre(Genre::MASCULIN)
{}



Personnage::Personnage(string prenom, string nom, Genre genre):_nom(nom),
                                                           _prenom(prenom),
                                                           _age(age),
                                                           _genre(genre)
{}

Personnage::Personnage(string prenom, string nom, int age, Genre genre):
                                                           _nom(nom),
                                                           _prenom(prenom),
                                                           _age(age),
                                                           _genre(genre)
{}

Personnage::Personnage(const Personnage & pers):_nom(pers._nom),
                                                _prenom(pers._prenom),
                                                _age(pers._age),
                                                _genre(pers._genre)
{}

3- message fonction du genre dans les constructeurs en utilisant une structure de contrôle conditionnelle

cout << "Le personnage " << _prenom << " " << _nom << " est ";
if(_genre == GENRE::MASCULIN)
{
    cout << " né ";
}
else
{
    cout << " née ";
}
cout << " et il a déjà " << _age << "ans.\n";

6. Section 8 Mise en œuvre de méthodes

6.1. Question 8.1

On ajoute une simple méthode pour l'affichage. Cette méthode retourne une chaine de caractères

1- Spécification

#include<string>
using namespace std;

class Personnage
{
...
public:
...
string identite();

};

2- Mise en œuvre. La concaténation de chaines de caractères se fait avec l'opérateur + et la conversion d'un entier en chaîne de caractères avec to_string(int) (C++ supérieur à C++11, vous travaillez en C++14)

string Personnage::identite()
{
   return "Salut, je m'appelle " + _prenom 
                                 + " " 
                                 + _nom 
                                 + " et j'ai " 
                                 + tostring(_age) 
                                 + " ans.";
}


6.2. Question 8.2

Dans le main(), on peut l'utiliser comme cela.

int main()
{
   Personnage tony("Tony","Stark",40,Genre::MASCULIN);
   cout << tony.identite() << '\n';

   Personnage leia("Leia","Skywalker",25,Genre::FEMININ);
   string leiaIdentite = leia.identite(); // stocke l'identite dans une variable;
   cout << leiaIdentite << '\n';
}

6.3. Question 8.3

En C++ on peut utiliser des booléens bool en opposition à C où les booléens n'existent pas.

1- Spécification

class Personnage
{
...
public:
...
bool estVieux();

};

2- Mise en œuvre.

bool  Personnage::estVieux()
{
  return _age >= 80;
}

6.4. Question 8.4

1- Spécification

class Personnage
{
...
public:
...
bool estPlusVieuxQue(Personnage * pers);

};

2- Mise en œuvre.

bool  Personnage::estPlusVieuxQue(Personnage * pers)
{
  return _age > pers->age(); // on peut également écrire  _age > pers->_age;
}

6.5. Question 8.5

Dans le main, exemple 1 (alloc statique)

int main()
{
   Personnage yoda("Yoda","Maitre",800,Genre::MASCULIN);
   Personnage leia("Leia","Skywalker",25,Genre::FEMININ);
   cout << yoda.prenom() << " est-il vieux ?" ;
   if(yoda.estVieux())
   {
     cout << "oui.";
   }
   else
   {
     cout << "non.";
   }  
   cout << leia.prenom() << " est-elle plus vieille que " << yoda.prenom() << "? ";
   // attention estPlusVieuxQue attend un paramètre pointeur 
   // contenant l'adresse de yoda, donc on passe en paramètre &yoda et non pas yoda
   if(leia.estPlusVieuxQue(&yoda)) 
   {
     cout << "oui.";
   }
   else
   {
     cout << "non.";
   }  
}

Dans le main, exemple 2 (alloc dynamique)

int main()
{
   Personnage * yoda = new Personnage("Yoda","Maitre",800,Genre::MASCULIN);
   Personnage * leia = new Personnage("Leia","Skywalker",25,Genre::FEMININ);
   cout << yoda->prenom() << " est-il vieux ?" ;
   if(yoda->estVieux())
   {
     cout << "oui.";
   }
   else
   {
     cout << "non.";
   }  
   cout << leia->prenom() << " est-elle plus vieille que " << yoda->prenom() << "? ";

   if(leia->estPlusVieuxQue(yoda)) 
   {
     cout << "oui.";
   }
   else
   {
     cout << "non.";
   }  
   // on oublie pas de detruire les objets
   delete leia; leia=nullptr;
   delete yoda; yoda=nullptr;
}

7. Section 9 La classe Histoire

7.1. Question 9.1

Pensez à utiliser les menus de NetBeans pour sélectionner New C++ Class, NetBeans génèrera les deux fichiers Histoire.h et Histoire.cpp

7.2. Question 9.2

#include<vector>
#include<string>
#include"Personnage.h"

using namespace std;

class Histoire
{
  private:
    string _titre;
    vector<Personnage *> _personnages;


};

7.3. Question 9.3

C'est la première fois que nous voyons une méthode privée. Cette méthode ne peut donc être utilisée qu'au sein de la classe Histoire. Elle réalise une opération intermédiaire, elle ne peut être appelée que dans une autre méthode de Histoire.

1- Spécification

class Histoire
{
  private:
    string _titre;
    vector<Personnage *> _personnages;
    void stockePersonnage(Personnage * p);

};

2- Mise en œuvre dans Histoire.cpp

On créé une place de plus à la fin du vecteur pour insérer le nouveau Personnage.

void Histoire::stockePersonnage(Personnage * p)
{
  _personnages.push_back(p);
}

7.4. Question 9.4

1- Spécification

class Histoire
{
  private:
    string _titre;
    vector<Personnage *> _personnages;
    void stockePersonnage(Personnage * p);
  public:
    Personnage * creerPersonnage(string nom, string prenom,int age, Genre genre);

};

2- Mise en œuvre

Ici on voit tout l'intérêt de l'allocation dynamique. creerPersonnage va créer un Personnage et retourner son adresse afin que l'objet puisse être utilisé ultérieurement une fois stocké. Avec une allocation statique, c'est tout bonnement impossible. Pour stocker le personnage, j'utilise la méthode stockePersonnage dont c'est l'objectif. Attention, la classe Histoire créé ainsi des objets en allocation dynamique. Toujours pensez qu'il faudra trouver un moyen par la suite de détruire ces objets (voir Questions 9.9 et 9.10).

Personnage * Histoire::creerPersonnage(string nom, string prenom,int age, Genre genre)
{
  Personnage * nouveauPersonnage = new Personnage(nom,prenom,age,genre);
  stockePersonnage(nouveauPersonnage);
  return nouveauPersonnage;
}

7.5. Question 9.5

1- Spécification

class Histoire
{

  public:
    Histoire();

};

2- Mise en œuvre

On appelle successivement et dans le bon ordre les constructeurs par défauts de _titre et de _personnages. Par défaut ces deux constructeurs répondent à la question (titre vide, vecteur vide).

Histoire::Histoire(): _titre(), _personnages() 
{}

7.6. Question 9.6

1- Spécification

class Histoire
{

  public:
    long nombreDePersonnages();

};

2- Mise en œuvre

On retourne le nombre de personnages dans le vecteur _personnages. C'est donné par la méthode size() appliquée au vecteur _personnages.

long Histoire::nombreDePersonnages()
{
  return _personnages.size();
}

7.7. Question 9.7

1- Spécification

class Histoire
{
  private:
    string _titre;
  public:
    string titre(); // accesseur/getter
    void changeTitre(string nouveauTitre); //mutateur/setter
};

2- Mise en œuvre

string Histoire::titre()
{
   return _titre;
}
void Histoire::changeTitre(string nouveauTitre)
{
  _titre = nouveauTitre;
}

7.8. Question 9.8

1- Spécification

class Histoire
{

  public:
    void afficherPersonnages(int age); // methode qui ne retourne rien: void
};

2- Mise en œuvre L'idée est d'itérer sur les éléments du vecteur _personnages et de vérifier l'âge du personnage pour savoir s'il faut l'afficher ou pas.

void Histoire::afficherPersonnages(int age)
{
   for(size_t i = 0; i < _personnages.size(); ++i)
   {
     if(_personnages.at(i)->age() <= age) // on peut utiliser _personnages[i] mais c'est moins sauf
     {
        cout << _personnages.at(i)->prenom() << " " << _personnages.at(i)->nom() << '\n';
     }

   }
}

7.9. Question 9.9

1- Spécification

class Histoire
{

  private:
    void effacerTousLesPersonnages();
};

2- Mise en œuvre On doit effacer les personnages qui sont stockés dans le vecteur Personnage. Il ne suffit pas d'effacer le vecteur (avec la méthode clear()) car on n'aurait effacé que les pointeurs et les objets pointés seraient toujours présents en mémoire mais non accessible (fuite de mémoire). Il nous faut donc appeler sur chaque élément du vecteur l'opérateur delete. Ici on voit typiquement la différence entre C++ et un langage type Python ou Java. Ces deux derniers ne nécessitent pas l'utilisation explicite de delete. Dans ces langages on aurait vider le vecteur uniquement. Ensuite un système propre à Java et Python détecte que des objets sont inaccessibles dans le programme et les détruit automatiquement. Ce système s'appelle un ramasse miette (Eng: Garbage collector). Très pratique pour le programmeur, il a néanmoins l'inconvénient de rendre Java et Python moins performants (entre autres choses). En C++, pas de ramasse-miette, le programmeur doit ramasser ses propres miettes !!

void Histoire::effacerTousLesPersonnages()
{
   for(size_t i = 0; i < _personnages.size(); ++i)
   {
     if(_personnages.at(i) != nullptr) // je verifie que le pointeur n'est pas nul, au cas où...
     {
       delete _personnages.at(i);
     }
   }
   // je vide le vecteur de pointeur, une fois tous 
   // les objets pointés détruits
   _personnages.clear();
}

7.10. Question 9.10

1- Spécification

class Histoire
{

  public:
    ~Histoire();
};

2- Mise en œuvre La classe Histoire a deux attributs. Le premier est alloué statiquement, rien à faire. Le deuxième, lui, stocke des objets dynamiquement alloués par la méthode creerPersonnage. Il faut explicitement les effacer. Cela tombe bien la méthode effacerTousLesPersonnages fait exactement cela.

Histoire::~Histoire()
{
  effacerTousLesPersonnages();
}

7.11. Question 9.11

Remarquez que le choix des noms est important. En lisant le code suivant, vous comprenez aisément ce qui se passe dans le programme principal.

#include"Personnage.h"
#include"Histoire.h"

int main()
{
   Histoire monHistoire;
   monHistoire.changeTitre("Retour vers le futur");
   monHistoire.creerPersonnage("Marty","McFly",17,Genre::MASCULIN);
   monHistoire.creerPersonnage("Emmett","Brown",77,Genre::MASCULIN);
   monHistoire.creerPersonnage("Georges","McFly",47,Genre::MASCULIN);
   monHistoire.afficherPersonnages(1000);
   return 0; // ici le destructeur ~Histoire est appelé, qui va détruire les personnages
}

7.12. Question 9.12

1- Spécification

class Histoire
{

  public:
    Histoire(const Histoire & hist);
};

2- Mise en œuvre Quand on fait un constructeur par copie qui stocke des pointeurs on se pose toujours la question de savoir s'il faut copier uniquement les pointeurs (clone) ou si on doit également copier les objets pointés (deep clone). La réponse est que cela dépend du contexte. Dans le cadre la classe Histoire, elle est en charge de créer ses propres personnages avec creerPersonnage donc il est cohérent de faire une copie profonde. En fait, on est en train de dire que la classe Histoire est composée d'objets Personnage (c'est une relation de Composition en UML, voir le TD2).


Histoire::Histoire(const Histoire & hist):_titre(hist._titre),_personnages()
{
  for(size_t i = 0; i < hist.nombreDePersonnages(); ++i)
  {
    stockePersonnage(new Personnage(hist._personnages.at(i))); // ici je passe par le constructeur par copie de Personnage, plus élégant à écrire
    // autre facon: creerPersonnage(hist._personnages.at(i).prenom(),
    //                              hist._personnages.at(i).nom(),
    //                              hist._personnages.at(i).age(),
    //                              hist._personnages.at(i).genre()); // conceptuellement plus propre
  }

}

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

Created: 2023-04-04 mar. 17:07

Validate