Vous êtes sur la page 1sur 52

Université Cadi Ayyad

Ecole Supérieure de Technologie – Safi

Cours de programmation orientée objet


(C++)

Pr. SAID EL ABDELLAOUI


Elabdellaoui.said@yahoo.fr

Cours 3 : Polymorphisme & Collections

Mohamed
El Nabil
Abdellaoui SaïdSaidi 2020/ 2021
OLYMORPHISME

2
POLYMORPHISME (UNIVERSEL D’INCLUSION)

 Grâce à l’héritage, le même code pourra être appliqué à


un Magicien, un Guerrier, ... qui sont des Personnage.
 La façon dont un Personnage en rencontre un autre peut
prendre plusieurs formes : le saluer (Magicien), le frapper
(Guerrier), le voler (Voleur).

 Grâce au polymorphisme, le même code appliqué à


différents personnages pourra avoir un comportement
différent, propre à chacun

Personnage p1, p2;


// ...
p1.rencontrer(p2);

Mohamed
El Nabil
Abdellaoui SaïdSaidi 3
RÉSOLUTION DES LIENS (STATIQUE)

 En C++, c’est le type apparent de la variable qui détermine la méthode à exécuter :


 résolution statique des liens

class Personnage { void faire_rencontrer( Personnage un,


public: Personnage autre) {
// ... cout << un.get_nom() << " rencontre "
void rencontrer(Personnage p) const << autre.get_nom() << " : ";
{ cout << "Bonjour !" << endl; } un.rencontrer(autre);
}; }
//============================= //=============================
class Guerrier : public Personnage { int main() {
public: Guerrier g;
void rencontrer(Personnage p) const Voleur v;
{ cout << "Boum !" << endl; } faire_rencontrer(g, v);
}; }

Bonjour !!

Mohamed
El Nabil
Abdellaoui SaïdSaidi 4
RÉSOLUTION DES LIENS (DYNAMIQUE)

 Il pourrait dans certains cas sembler plus naturel de choisir la méthode correspondant à la nature réelle de
l’instance (type effectif ).

 Dans ces cas, il faut permettre la résolution dynamique des liens pour que le choix de la méthode à exécuter
se fait à l’exécution et en fonction de la nature réelle des instances

 2 ingrédients pour cela :

méthodes virtuelles

et

références/pointeurs

Mohamed
El Nabil
Abdellaoui SaïdSaidi 5
DÉCLARATION DES MÉTHODES VIRTUELLES

 En C++, on indique au compilateur qu’une méthode peut faire l’objet d’une résolution dynamique des liens
en la déclarant comme virtuelle (mot clé virtual)
 Cette déclaration doit se faire dans la classe la plus générale qui admet cette méthode (c’est-à-dire lors du
prototypage d’origine)
 Les redéfinitions éventuelles dans les sous-classes seront aussi considérées comme virtuelles par transitivité.

Syntaxe :

virtual Type nom_fonction(liste de paramètres) [const];

Exemple :

class Personnage { class Personnage {


// ... // ...
virtual void rencontrer(Personnage autre) virtual void rencontrer(Personnage& autre) const
{ cout << "Bonjour !" << endl; } { cout << "Bonjour !" << endl; }
}; };

Version suffisante Version parfaite

Mohamed
El Nabil
Abdellaoui SaïdSaidi 6
RETOUR SUR L’EXEMPLE (1)

class Personnage { void faire_rencontrer( Personnage un,


public: Personnage autre) {
// ... cout << un.get_nom() << " rencontre "
Virtual void rencontrer(Personnage& p) const << autre.get_nom() << " : ";
{ cout << "Bonjour !" << endl; } un.rencontrer(autre);
}; }

class Guerrier : public Personnage { int main() {


public: Guerrier g;
void rencontrer(Personnage& p) const Voleur v;
{ cout << "Boum !" << endl; } faire_rencontrer(g, v);
}; }

... il manque encore un petit quelque chose pour que ça marche !!!!

Mohamed
El Nabil
Abdellaoui SaïdSaidi 7
RETOUR SUR L’EXEMPLE (2)

 Attention ! Il faut passer un par référence pour que la fonction faire_rencontrer agisse sur l’instance
d’origine !

class Personnage { void faire_rencontrer( Personnage const& un,


public: Personnage const& autre) {
// ... cout << un.get_nom() << " rencontre "
Virtual void rencontrer(Personnage& p) const << autre.get_nom() << " : ";
{ cout << "Bonjour !" << endl; } un.rencontrer(autre);
}; }

class Guerrier : public Personnage { int main() {


public: Guerrier g;
void rencontrer(Personnage& p) const Voleur v;
{ cout << "Boum !" << endl; } faire_rencontrer(g, v);
}; }

Cette fois ci tout fonctionne comme on veut !!

Mohamed
El Nabil
Abdellaoui SaïdSaidi 8
VIRTUELLE / NON VIRTUELLE : EXEMPLE

class Mammifere {
public: Mammifere() { cout << "Un nouveau mammifère est né !" << endl; }
virtual ~Mammifere() {cout << "Un mammifère est en train de mourir :(" << endl; }
void manger() const {cout << "Miam... croumf !" << endl; }
virtual void avancer() const {cout << "Un grand pas pour l'humanité." << endl;}};

class Dauphin : public Mammifere {


public: Dauphin () {cout << "Coui, Couic !" << endl; }
~Dauphin() {cout << "Flipper, c'est fini..." << endl; }
void manger() const {cout << "Sglups, un poisson." << endl; }
void avancer() const {cout << "Je nage." << endl; }};

int main() {
Mammifere* lui(new Dauphin());
lui->avancer();
lui->manger();
delete lui;
return 0; }

Mohamed
El Nabil
Abdellaoui SaïdSaidi 9
VIRTUELLE / NON VIRTUELLE : EXEMPLE

 Que produit le code suivant ? int main() {


Mammifere* lui(new Dauphin());
lui->avancer();
lui->manger();
delete lui;
return 0; }
Un nouveau mammifère est né!
Coui, Couic !
Je nage.
Miam... croumf ! Mammifere::Mammifere()
Flipper, c’est fini... Dauphin::Dauphin()
Un mammifère est en train de mourir :( Dauphin::avancer()
Mammifere::manger()
 Et si le destructeur de Mammifere n’avait pas été virtuel ? Dauphin::~Dauphin()
Mammifere::~Mammifere()
Un nouveau mammifère est né!
Coui, Couic !
Je nage.
Miam... croumf !
Un mammifère est en train de mourir :(

Mohamed
El Nabil
Abdellaoui SaïdSaidi 10
C++11 OVERRIDE ET FINAL

 En C++11 , le programmeur peut (optionnel) indiquer ses intentions lors de la (re)déclaration d’une
méthode :
 le qualificatif override pour dire qu’il pense substituer/redéfinir une méthode virtuelle
 le qualificatif final pour empêcher la substitution/redéfinition future d’une méthode virtuelle.
 Exemple :

class A {
// ...
virtual void f1();
virtual void f2() const;
void f3(); // non virtuelle (oubli ?)
virtual void f4() final; // pas de redéfinition
};

class B : public A {
// ...
virtual void f1() override; // OK
virtual void fl() override; // Erreur faute de frappe : 1 <-> l
virtual void f2() override; // Erreur: a oublié le const
void f3() override; // Erreur: non virtuelle
virtual void f4(); // Erreur : f4 était final
};

Mohamed
El Nabil
Abdellaoui SaïdSaidi 11
LASSE ABSTRAITE

12
MÉTHODES VIRTUELLES PURES : PROBLÈME

 Au sommet d’une hiérarchie de classe, il n’est pas toujours possible de :


 Donner une définition générale de certaines méthodes, compatibles avec toutes les
sous-classes,
 ...même si l’on sait que toutes ces sous-classes vont effectivement implémenter ces
méthodes
 Exemple :

Class FigureFermee

+ double surface ()

Class Triangle Class Cercle


+ double surface () + double surface ()

Mohamed
El Nabil
Abdellaoui SaïdSaidi 13
BESOIN DE MÉTHODES VIRTUELLES PURES : EXEMPLE
 Exemple : class FigureFermee {
// ...
// difficile à définir à ce niveau !..
virtual double surface() const { ??? }
} };

Class Triangle : public FigureFermee { class Cercle : public FigureFermee {


private double L , H; private double x,y, r;
// surface. // surface.
double surface() { double surface() {
return H * L /2; return PI*r*r;
} }; } };

main (){
Cercle C1; Triangle T1;
// surfaces
C1.surface();T1.surface();
} }

Mohamed
El Nabil
Abdellaoui SaïdSaidi 14
MÉTHODES VIRTUELLES PURES : AUTRE EXEMPLE (5)

 La solution :

 ajouter une méthode quelconque définie arbitrairement :

class Personnage {
// ...
// On n'affiche rien : corps de la méthode vide
virtual void afficher() const = 0;
// ...
};

 C’est une très mauvaise idée


 Mauvais modèle de la réalité (affichage incorrect si une sous-classe ne redéfinit pas la méthode
: personnages fantômes !)
 Cette solution n’impose pas que la méthode afficher soit redéfinie

Mohamed
El Nabil
Abdellaoui SaïdSaidi 15
MÉTHODES VIRTUELLES PURES : DÉFINITION ET SYNTAXE

 Une méthode virtuelle pure, ou abstraite :


 sert à imposer aux sous-classes (non abstraites) qu’elles doivent redéfinir la
méthode virtuelle héritée
 est signalée par un = 0 en fin de prototype,
 est, en général, incomplètement spécifiée : il n’y a très souvent pas de définition
dans la classe où elle est introduite (pas de corps).
 Syntaxe :

virtual Type nom_methode(liste de paramètres) = 0;

 Exemple :
class Personnage {
// ...
virtual void afficher() const = 0;
// ...
};

Mohamed
El Nabil
Abdellaoui SaïdSaidi 16
CLASSES ABSTRAITES

 Exemple : class FigureFermee {


// ...
// difficile à définir à ce niveau !..
virtual double surface() const = 0;
};

class Triangle:public FigureFermee { class Cercle:FigureFermee { main (){

private double L , H; private double x,y, r; Cercle C1; Triangle T1;

// surface. // surface. // surfaces

double surface() { double surface() { C1.surface();

return H * L /2; } return PI*r*r; } T1.surface();

}; }; }

 Une classe abstraite est une classe contenant au moins une méthode virtuelle pure.
 Elle ne peut être instanciée
 Ses sous-classes restent abstraites tant qu’elles ne fournissent pas les définitions de toutes les méthodes
virtuelles pures dont elles héritent.

Mohamed
El Nabil
Abdellaoui SaïdSaidi 17
CLASSES ABSTRAITES : EXEMPLE

 Une autre équipe crée la sous-classe Guerrier de Personnage et veut l’utiliser :

Jeu jeu;
jeu.ajouter_personnage (new Guerrier(...));

 S’ils ont oublié de définir la méthode afficher, le code ci-dessus génère une erreur de
compilation car on ne peut pas créer d’instance de Guerrier :

cannot allocate an object of abstract type ‘Guerrier’


because the following virtual functions are pure within ‘Guerrier’:
virtual void Guerrier::afficher()

Mohamed
El Nabil
Abdellaoui SaïdSaidi 18
CLASSES ABSTRAITES : AUTRE EXEMPLE

class Polygone: public FigureFermee {


class Cercle: public FigureFermee {
public:
public:
double perimetre() const override {
double surface() const override {
double p(0.0);
return M_PI * rayon * rayon;
for (auto cote : cotes) {
}
p += cote;
double perimetre() const override {
}
return 2.0 * M_PI * rayon;
return p;
}
}
protected:
protected:
double rayon;
vector <double> cotes;
};
};

Cercle n’est pas une classe abstraite Polygone reste par contre une classe abstraite

Mohamed
El Nabil
Abdellaoui SaïdSaidi 19
MART OINTERS

20
Introduction

 En C++, il existe plusieurs sorte de pointeurs :


 Les références : totalement générées en interne par le compilateur. Très sures, donc;
mais sont fondamentalement différentes des vrais pointeurs.

 Les pointeurs à la C (build-in pointers) : les plus puissants (peuvent tout faire) mais
les plus dangereux.

 C++11, les pointeurs intelligents (smart pointers) : gérés par le programmeur; mais
avec garde-fous. Il en existe 3 : unique_ptr, shared_ptr, weak_ptr (avec
#include<memory>)

Mohamed
El Nabil
Abdellaoui SaïdSaidi 21
Les références

 Une référence est un autre nom pour un objet existant, un synonyme, un alias. Une référence
permet donc de désigner un objet indirectement. C’est exactement ce que l’on utilise lors d’un
passage par référence. La déclaration d’une référence se fait selon la syntaxe suivante :

type & nom_reference(identificateur);

 Après une telle déclaration, nom_reference peut être utilisé partout où identificateur peut l’être
 Exemple :

int val(1);
int& x(val);

Mohamed
El Nabil
Abdellaoui SaïdSaidi 22
Les références

Attention aux pièges !

 Signification de l’opérateur = :

int i(3); int i(3);


int& j(i); //alias int j(i); //copie
/* i et j sont les mêmes /* i et j vivent leur vie séparément
i=4; //j aussi vaut 4 i=4; //j vaut encore 3
j=6; //i aussi vaut 6 j=6; //i vaut encore 4

 Sémantique de const :

int i(3);
const int& j(i); /* i et j sont les mêmes, on ne peut pas changer la valeur
Via j
* mais on peut le faire par ailleurs */
i=12; // NON
j=12;
// OUI, et j AUSSI vaut 12 !

Mohamed
El Nabil
Abdellaoui SaïdSaidi 23
Les Pointeurs

 Un pointeur est une variable qui contient l’adresse d’un autre objet informatique.

Ajouter une page dans le carnet (mais cela ne veut pas


Déclarer un int* ptr; dire qu’il y a une adresse écrite dessus !)
pointeur ptr
Recopier sur la page ptr l’adresse d’une maison qui
Affecter un ptr = &x; existe déjà (mais ptr n’est pas la maison, c’est juste la
pointeur ptr page qui contient l’adresse de cette maison !)
Aller construire une maison quelque part et noter son
Allouer un pointeur ptr = new int(123); adresse sur la page ptr (mais ptr n’est pas la maison !)
ptr
Aller détruire la maison dont l’adresse est écrite en page
libérer un pointeur ptr. Cela ne veut pas dire que l’on a effacé l’adresse sur
ptr (en fait, c’est « delete ptr; la page ptr !! mais juste que cette maison n’existe plus.
libérer l’adresse Cela ne veut pas non plus dire que toutes les pages qui
pointée par le ont la même adresse que celle inscrite sur la page ptr
pointeur » ptr) n’ont plus rien (mais juste que l’adresse qu’elles
contiennent n’est plus valide)

Mohamed
El Nabil
Abdellaoui SaïdSaidi 24
Les Pointeurs
 C++ possède deux opérateurs particuliers en relation avec les pointeurs : & et *.
 & est l’opérateur qui : retourne l’adresse mémoire de la valeur d’une variable
 Si x est de type type, &x est de type type* (pointeur sur type).

 Exemple :

int x(3);
int* px(nullptr);
px = &x;

 * est l’opérateur qui retourne la valeur pointée par une variable pointeur.
 Si px est de type type*, (*px) est la valeur de type type pointée par px.

 Exemple :

int x(3); // x est de type entier


int* px(nullptr); // px est un pointeur sur entier
px = &x; // px pointe sur la variable x
cout << *px << endl; // affiche la valeur pointée par px : 3

Mohamed
El Nabil
Abdellaoui SaïdSaidi 25
Les Pointeurs

C’est pas la même chose !!!

Type& id est une référence sur une variable id &id est l’adresse de la variable id par exemple en
dans le passage par référence d’une fonction affectation d’un pointeur.

Type* id déclare une variable id comme un *id (ou id est un pointeur ) représente le contenu
pointeur sur un type de base type de l’endroit pointé par id

Mohamed
El Nabil
Abdellaoui SaïdSaidi 26
Unique_ptr

 unique_ptr pointent sur une zone mémoire n’ayant qu’un seul pointeur (‘un seul propriétaire’).
==> évite les confusions
 Ne peut pas être copié mais peut être ‘’déplacé’’, ‘’transmis’’ plus loin
 Note : si on veut libérer un unique_ptr avant le garbage collector (c’est-à-dire faire le delete nous
même), on peut utiliser la fonction spécifique reset() :

Remet en plus ptr à nullptr ptr.reset()

 Exemple :
#include <memory>
//…
unique_ptr<int> px(new int(20));
//…
cout << *px << endl;

Vector<unique_ptr<string>> noms({new string("Pierre"), new string ("Paul")})

Mohamed
El Nabil
Abdellaoui SaïdSaidi 27
OLLECTION HÉTÉROGÈNE
&
OINTOR OLLECTIOS

28
ECTOR ARRAY

29
Les tableaux en C++

 En C++, on utilise :

Taille initiale connue a priori

NON OUI

OUI vector (vector)


Taille pouvant varier lors
de l’usage du tableau array (C++11)
NON (vector)
Tableau (à la C)

 Dans un premier temps, nous allons nous intéresser aux tableaux dynamiques (vector)
 Viendront ensuite les tableaux de taille fixe (array)

Mohamed
El Nabil
Abdellaoui SaïdSaidi 30
Les tableaux en C++

 Un tableau dynamique, est une collection de données homogènes, dont le nombre peut changer au
cours du déroulement du programme, par exemple lorsqu’on ajoute ou retire des éléments au/du tableau.
 Les tableaux dynamiques sont définis en C++ par le biais du type : vector
 Pour les utiliser, il faut tout d’abord importer les définitions associées à l’aide d’un include :

#include <vector>

 Une variable correspondant à un tableau dynamique se déclare de la façon suivante :


vector<type> identificateur;

où identificateur est le nom du tableau et type correspond au type des éléments du tableau.
 Exemple :
#include <vector>
vector<int>tableau;

type des éléments peut être n’importe quel type C++ valide
 Exemple (à ne pas suivre !) d’erreur classique :

vector <double> v; // NON !! v[0] n'existe pas encore !


v[0] =5.4;

Mohamed
El Nabil
Abdellaoui SaïdSaidi 31
Initialisation d’un tableau dynamique
 En C++11, il y a 5 façons d’initialiser un tableau dynamique :

1. vide
vector <int> tab;

2. avec une taille initiale donnée et tous les éléments « nuls »

vector <int> tab(5);

3. avec une taille initiale donnée et tous les éléments à une même valeur donnée

vector <int> tab(5,1);

4. avec un ensemble de valeurs initiales

vector <int> tab ({20,35,26,38,22});

5. avec une copie d’un autre tableau

vector <int> tab(tab2);

Mohamed
El Nabil
Abdellaoui SaïdSaidi 32
Itération sur un tableau (C++11)
 Si on souhaite parcourir les éléments du tableau sans faire des modifications :
for (auto nom_de_variable : tableau)

 Si on souhaite parcourir les éléments du tableau avec modifications :


for (auto & nom_de_variable : tableau)

où type est le type des éléments contenus dans le tableau.

 Exemples :

vector < int > ages(5);


//remplissage
for ( auto& age : ages ) {
cout<<"Age de l'employé suivant ? ";
cin>>age; }
//affichage
cout<<"Age des employés : "<<endl;
for ( auto age : ages) {
cout<<""<<age<<endl;
}

Mohamed
El Nabil
Abdellaoui SaïdSaidi 33
Fonctions spécifiques

 Un certain nombre d’opérations sont directement attachées au type vector.


 L’utilisation de ces opérations spécifiques se fait avec la syntaxe suivante :

nom_de_tableau.nom_de_fonction(arg1, arg2, ...);

 Quelques fonctions disponibles pour un tableau dynamique nommé tableau, de type vector<type> :
 size() : renvoie la taille de tableau (type de retour : size_t)
 front() : renvoie une référence au 1er élément
tableau.front() est donc équivalent à tableau[0]
 back() : renvoie une référence au dernier élément.
tableau.back() est donc équivalent à tableau [tableau.size()-1]
 empty() : détermine si tableau est vide ou non (bool).
 clear() :supprime tous les éléments de tableau (et le transforme donc en
un tableau vide). Pas de (type de) retour.
 pop_back() : supprime le dernier élément de tableau. Pas de retour.
 push_back(Val) : ajoute un nouvel élément de valeur Val à la fin de tableau. Pas de retour.

Mohamed
El Nabil
Abdellaoui SaïdSaidi 34
push_back et pop_back

vector <double> v(3,4.5);


v.pop_back();
v.push_back(5.6);
v.push_back(6.7);
v.pop_back();

4,5 4,5 4,5 4,5

4,5 4,5 4,5 4,5

4,5 5.6 5.6 5.6

6,7

Mohamed
El Nabil
Abdellaoui SaïdSaidi 35
Les tableaux multidimensionnels

 Comment déclarer un tableau à plusieurs dimensions ?

 On ajoute simplement un niveau de plus : C’est en fait un tableau de tableaux...

 Exemple :

vector <vector<int>> tab ( 5, vector<int>(6) );

correspond à la déclaration d’un tableau de 5 tableaux de 6 entiers

 tab[i] est donc un « vector<int> », c’est-à-dire un tableau dynamique d’entiers (qui, au départ, en
contient 6)

 tab[i][j] sera alors le (j +1)-ième élément de ce tableau.

Mohamed
El Nabil
Abdellaoui SaïdSaidi 36
Les tableaux multidimensionnels

 On a l’habitude de se représenter tab[i][j] comme l’élément de la (i+1) ème ligne et de la (j+1)


ème colonne.

 Mais attention ! Un vector<vector<int>> n’est pas une matrice, mais un tableau dynamique de
tableaux dynamiques d’entiers (pas nécessairement tous de la même taille !).

tableau[][J]

0 1 2 3 42
4 5 6
tableau[i]

tableau[2][1]
7 8
9 0 1
tableau[2] : 7 8

Mohamed
El Nabil
Abdellaoui SaïdSaidi 37
Déclaration d’un tableau de taille fixe
 En C++11 , le type « tableau de taille fixe » est défini dans la bibliothèque array.
 Pour les utiliser, il faut ajouter en début de programme :

#include <array>

 Une variable correspondant à un tableau de taille fixe se déclare de la façon suivante :

array <type, taille> identificateur;

 où identificateur est le nom du tableau, type correspond au type des éléments du tableau et taille est
le nombre d’éléments que contient le tableau. Ce nombre doit être connu au moment où on écrit le
programme ( sinon vector)
 Exemple :

#include <array>
...
array<double,3>vecteur_3d;

Mohamed
El Nabil
Abdellaoui SaïdSaidi 38
Déclaration d’un tableau de taille fixe
 Comme pour les tableaux dynamiques, un tableau de taille fixe peut être initialisé directement
lors de sa déclaration :
array<type, taille> identificateur({val1, ... , val});

ou

array<type, taille> identificateur = {val1, ... , val};

 Exemple :

const size_t taille(5); Age


array<int,taille>ages( {20,35,26,38,22} );
20
// ou :
array<int,taille>ages={20,35,26,38,22} ; 35
26
Un array non initialisé contient « n’importe quoi ». 38
22

Mohamed
El Nabil
Abdellaoui SaïdSaidi 39
Pour résumer

Tableaux dynamiques Tableaux statiques

#incluse <vector> #incluse <array>


vector<double> tab;
vector<double> tab2(5); vector<double,5> tab2

tab[i][j]
Tab.size()
for (auto element : tab)
for (auto& element : tab)

Tab.push_back(x); -
Tab.pop_back(); -

vector<vector<int>> array<array<int,3>,4>
tableau( { {0,1,2,3,42}, matrice= { 0,1,2,
{4,5,6}, 3,4,5,
{7,8}, 6,7,8,
{9,0,1}} ); 9,0,1 };

Mohamed
El Nabil
Abdellaoui SaïdSaidi 40
Polymorphisme

Un exemple :

 Nous avons vu jusqu’à maintenant que :


 l’héritage et les méthodes virtuelles permettent de mettre en œuvre des traitements génériques
sur les instances d’une hiérarchie de classes (polymorphisme).
 les fonctions/méthodes génériques doivent utiliser des arguments passés par référence pour
que le traitement se fasse en fonction de la nature réelle de l’instance
 Qu’en est-il si un tel traitement (générique) doit porter sur un ensemble d’instances d’une hiérarchie
de classe?
 Collection hétérogène (au sens où le comportement spécifique de chaque instance de la collection
peut être différent)

Mohamed
El Nabil
Abdellaoui SaïdSaidi 41
Collections

 On pourrait par exemple souhaiter plutôt écrire quelque chose comme :

class Jeu {
public:
void afficher() const;
void ajouter_personnage(const Personnage&);
private:
vector<Personnage> personnages;
};

 Les instances contenues dans l’attribut personnages font partie d’une même hiérarchie de classe, mais
sont de nature hétérogène (Guerrier, Magicien, ...).
 On pourrait par exemple vouloir que, si personnages[i] est un guerrier, la méthode
personnages[i].afficher() soit bien celle de la sous-classe Guerrier.

Problème : Si l’on veut une collection avec comportement polymorphique des éléments, il faut une
collection de pointeurs ou de références

Mohamed
El Nabil
Abdellaoui SaïdSaidi 42
Collections

Personnages :

.Affiche() .Affiche() .Affiche() .Affiche() .Affiche() .Affiche()

Exécution : Personnage Personnage Personnage Personnage Personnage ….!

class Jeu {
private : vector<Personnage> personnages;
public :
void ajouter_personnage(const Personnage& per){
Personnages.push_back(per);
}

void afficher() const { for(auto element : personnages)


{element.Affiche(); } }
}

Mohamed
El Nabil
Abdellaoui SaïdSaidi 43
Collections

Personnages :

.Affiche() .Affiche() .Affiche() .Affiche() .Affiche() .Affiche()

Exécution : Personnage Personnage Personnage Personnage Personnage ….!

class Jeu {
private : vector<Personnage> personnages;
public :
void ajouter_personnage(const Personnage& per){
Personnages.push_back(per);
}

void afficher() const { for(auto element : personnages)


{element.Affiche(); } }
virtual
}
Affiche();

Mohamed
El Nabil
Abdellaoui SaïdSaidi 44
Collection de pointeurs

 Malheureusement on ne peut pas mettre de référence dans un vector.


 La solution à ce problème consiste donc à passer par vecteur de pointeurs :

class Jeu { #include <memory>


//…. class Jeu {//….
vector<Personnage*> personnages; Vector<unique_ptr<Personnage>> personnages;
}; };

 Notez que donc seuls les pointeurs, c’est-à-dire les adresses des instances, sont stockées dans la
collections, et non plus les instances elles-mêmes :

….

Guerrier Magicien Guerrier …. Guerrier

Mohamed
El Nabil
Abdellaoui SaïdSaidi 45
Exemple complet : classes

 On aurait donc :

class Jeu {
public :
Void afficher() const;
Void ajouter_personnage(Personnage *);
private :
Vector<unique_ptr<Personnage>> personnages;
};

 L’instanciation de la collection est comme suit :

. Jeu jeu;
Jeu.ajouter_personnage(new Guerrier(…));

Mohamed
El Nabil
Abdellaoui SaïdSaidi 46
Collections

Personnages :

.Affiche() .Affiche() .Affiche() .Affiche() .Affiche() .Affiche()

Exécution : Voler ! Frapper! sourciller! Voler! sourciller! ….!

class Jeu {
private : vector<unique_ptr<Personnage>> personnages;
public :
void ajouter_personnage(const Personnage* per){
if(per!= nullptr)
Personnages.push_back(<unique_ptr<Personnage>> per);
}

virtual void afficher() const { for(auto element : personnages)


{element->Affiche(); }
}

Mohamed
El Nabil
Abdellaoui SaïdSaidi 47
Pointeur et intégrité des données

 Cette classes jeu comporte cependant un danger potentiel :


 Pour que tout fonction bien, il est nécessaire que les éléments pointés existent aussi longtemps
que leurs pointeurs.

….

….
Guerrier Magicien Guerrier Guerrier

 Attention !! La co-existence des pointeurs et des éléments pointés n’est cependant pas du tout
garantie ! Au programmeur de ne pas faire de bêtises.

Mohamed
El Nabil
Abdellaoui SaïdSaidi 48
Pointeurs et intégrité des données : mauvais exemple

// définition robuste de la fonction creer_magicien


Void creer_magicien(Jeu& jeu){
Magicien mago(…);
Jeu.ajouter_personnage(mago);
}
//……
main(){
Jeu mon_jeu;
Creer_magicien(mon_jeu);
mon_jeu->affiche();
}

 La fonction creer_magicien ajoute un nouveau magicien au jeu mon_jeu, mais par le biais d’une
variable locale (bouh !)
 Une fois l’exécution de creer_magicien terminée, la variable locale est détruite !!
 Attention !! le pointeur stocké dans le vecteur personnages existe toujours….

Mohamed
El Nabil
Abdellaoui SaïdSaidi 49
Allocation / désallocation dynamique

 La solution à ce problème est que l’utilisateur alloue dynamiquement une portion de mémoire qui
sera préservée après la fin du bloc où l’on crée l’instance.
 Exemple

// définition robuste de la fonction creer_magicien


Void creer_magicien(Jeu& jeu){
Jeu.ajouter_personnage(new Magicien(…));
}

 Grâce à l’utilisation de new, la mémoire allouée dynamiquement pour le magicien créé dans
creer_magicien est préservée à la fin de l’exécution de cette fonction.

Mohamed
El Nabil
Abdellaoui SaïdSaidi 50
Collection Pointeur
class Jeu {
private : vector<unique_ptr<Personnage>> personnages;
public : void ajouter_personnage(const Personnage* per){
if(per!= nullptr)
Personnages.push_back(<unique_ptr<Personnage>> per);
}
void afficher() const { for(auto const& element : personnages)
{element->Affiche(); }
}

class Jeu {
private : vector<Personnage*> personnages;
public :
void ajouter_personnage(const Personnage* per){
if(per!= nullptr)
Personnages.push_back(per);
}
void afficher() const { for(auto const& element : personnages)
{element->Affiche(); }
}

Mohamed
El Nabil
Abdellaoui SaïdSaidi 51

Vous aimerez peut-être aussi