Vous êtes sur la page 1sur 44

Module :

Programmation orientée objet en C++

hachanisafaa@gmail.com

1
Chapitre 4 :

Héritage : Complémets

Plan :
Attributs et méthodes de classes
Polymorphisme

2
Attributs de classes – Le problème
On veut modifier les classes produit précédentes afin d’attribuer
automatiquement une référence unique à tous les produits.

⇒Les constructeurs ne prennent plus comme paramètre une reference ref, et


l’attribut _ref est initialisé automatiquement par une valeur unique.

3
Attributs de classes – La solution

▶ Utiliser un compteur
▶ initialisé à 0 au début de l’exécution du programme;
▶ incrémenté à chaque instanciation de produit.

▶ Ce compteur est utilisé par le constructeur de produit pour récupérer une


valeur unique afin d’initialiser _ref.

▶ Ce compteur devrait être «caché» afin d’être visible des seules méthodes de
produit.

En faire un attribut private de produit?

4
Attributs de classes – Une solution

Code incorrect
Chaque instance de produit dispose d’une valeur de l’attribut _compteur…
alors qu’un compteur partagé (par toutes les instances) est requis. 5
Attributs de classe

Définition (Attribut / méthode d’instance) :


Un attribut d’instance est associé à une instance : chaque instance dispose
d’une valeur pour cet attribut. Une méthode d’instance s’applique sur une
instance.

Le compteur est un attribut de classe, il a une unique valeur pour toute les
instances.

Définition (Attribut de classe) :


Un attribut de classe est associé à une classe : toutes les instances de cette
classe «partagent» la même valeur pour cet attribut.
6
Attributs et méthodes de classe - Syntaxe

Syntaxe Attribut de classe :

▶ L’emploi du mot-clef static devant la déclaration d’un attribut fait de cet


attribut un attribut de classe.

▶ Sans mot-clef static, c’est un attribut d’instance.

7
Attributs de classe - Déclaration

8
Attributs de classe – Définition et initialisation

Attention :

La seule déclaration dans le fichier.hh ne suffit pas. Il faut aussi définir (et
initialiser) cet attribut dans le fichier.cc correspondant.

Cette définition (et initialisation) :

▶ doit être faite en dehors de tout bloc de code.


▶ a la forme syntaxique d’une déclaration (et initialisation) de variable qui
aurait pour nom NomClasse::NomAttribut

9
Attributs de classe – Définition et initialisation

À l’intérieur du code d’une méthode, on accède directement à un attribut de


classe de cette classe.

Écrire this->_compteur n’a aucun sens (même si ce n’est pas faux).


10
Attributs de classe

Généralités :

▶ Toutes les visibilités peuvent être appliquées aux attributs de classe.

▶ Si un attribut de classe est visible depuis une autre classe, on y accède en


écrivant NomClasse::NomAttribut.

▶ Un attribut de classe peut être constant, ce qui permet de définir des


constantes de classe.

▶ La valeur d’une constante de classe peut être donnée dans le fichier.hh si le


type de la constante est primitif.
11
Méthodes de classe

▶ Une méthode d’instance est appelée sur une instance.

▶ Le code de la méthode peut accéder à l’objet sur lequel elle a été appelée à
l’aide du pointeur this.

Parfois, on veut appeler une méthode sans fournir d’objet sur lequel appeler la
méthode.

Écrire une méthode de comparaison de 2 produits retournant vrai si l’objet


courant est moins cher, faux sinon (celui passé en paramètre est moins cher).
12
Méthodes de classe – Exemple

Écrire une méthode de comparaison de 2 produits retournant vrai si l’objet


courant est moins cher, faux sinon (celui passé en paramètre est moins cher).

⇒Les deux produits ont le même rôle dans la comparaison, pourtant, l’un des
deux est «privilégié» : on appelle la méthode sur cet objet.
13
Méthodes de classe

Définition (Méthode de classe) :

Une méthode de classe est appliquée sur sa classe (et non sur une instance de
sa classe).

Syntaxe méthode de classe :

▶ L’emploi du mot-clef static devant la déclaration d’une méthode fait de cette


méthode une méthode de classe.
NB: Devant la définition on ne mets pas le mot clés static.

▶ Sans mot-clef static, c’est une méthode d’instance.


14
Méthodes de classe

G.Énéralités (Méthode de classe) :

Une méthode de classe…

▶ ne peut accéder à l’objet courant this.

▶ ne peut accéder directement aux attributs (d’instance) ou appeler


directement des méthodes (d’instance) sur l’objet courant.

▶ peut accéder aux attributs de classe ou appeler des méthodes de classe.

15
Méthodes de classe

On définira une méthode de classe:

▶ Quand la méthode s’applique sur plusieurs instances, et qu’aucune de ces


instances n’a de rôle particulier.

▶ Quand la classe doit fournir un service sans nécessiter la moindre création


d’instance.

▶ …Quand, d’un point de vue «conception», il est préférable de rattacher un


comportement à la classe plutôt qu’à une instance donnée.

16
Méthodes de classe
Définir une méthode de classe comparaison pour la classe produit.

17
Exercice Attributs et méthodes de classe
Il arrive qu’une entreprise ait besoin de connaître le nombre d'objets qui ont été créés,
d'une classe produitX.

Donner une solution.


Pour y arriver, on crée alors un attribut statique compteur que l'on initialise à zéro. On
incrémente ensuite ce compteur dans les constructeurs de la classe et, bien sûr, on le
décrémente dans le destructeur.
Il nous faut respecter l'encapsulation (on ne veut pas que tout le monde puisse changer
le nombre d'objets). Il faut donc mettre le compteur dans la partie privée de la classe et
ajouter un accesseur (méthode qui retourne le nombre d’instances). Cet accesseur est
bien sûr une méthode statique !

18
Exercice Attributs et méthodes de classe
class ProduitX
{
public:
ProduitX(string ref);
//Plein de méthodes…
~ProduitX();
static int nombreInstances(); //Renvoie le nombre d'objets créés

private:
string _ref;
static int compteur;
}

19
Exercice Attributs et méthodes de classe

int ProduitX ::compteur = 0; //On initialise notre compteur à 0

ProduitX :: ProduitX(string ref)


:_ref(ref) {
++compteur; //Quand on crée un produit, on ajoute 1 au compteur }

ProduitX ::~ ProduitX() {


--compteur; //Et on enlève 1 au compteur lors de la destruction }

int ProduitX::nombreInstances() {
return compteur; //On renvoie simplement la valeur du compteur }

20
Exercice Attributs et méthodes de classe

int main()
{
//On crée deux produits
ProduitX goliath("Goliath001");
ProduitX lancelot("Lancelot005");

//Et on consulte notre compteur


cout << "Il y a actuellement " << ProduitX ::nombreInstances() << " ProduitX crées." <<
endl;
return 0;
}

21
Polymorphisme

On veut gérer le stock des différents produits (perrisable, culturel, multimedia et


standard).

On peut mémoriser chaque collection de produits dans une structure de donnée (vector:
bib stand du C++).

22
Polymorphisme – Exemple

La classe stock dispose d’une méthode


AfficherProduits qui parcourt les 4
vector et affiche les produits. Est-ce
une bonne solution ?

▶ Chaque méthode qui porte sur la totalité des produits du stock doit faire 4 boucles :
une pour le parcours de chaque vecteur de produit.

▶ La définition d’une nouvelle sous-classe de produit demande de modifier le code des


méthodes de la classe stock. 23
Polymorphisme – Solution

Utiliser un seul vector ?


Avec des objets de type Produit (classe mère).

Le vector ne peut pas contenir des éléments de type différents.


▶ Chaque élément du vector est une instance de produit (et uniquement de cette classe-là).

▶ Les éléments du vector doivent être du même type (produit) mais il peut «contenir» des
types différents (sous-classes de produit),
▶ Un pointeur d’une super-classe (produit) peut pointer sur des instances des sous-classes
(produit culturel, etc.)
24
Polymorphisme – Solution - le polymorphisme

25
Polymorphisme – Solution utilisant le polymorphisme

▶ Seule la classe produit est utilisée.

▶ Nécessite d’utiliser des pointeurs de produit et non des instances.

Quand le polymorphisme est utilisé, on ne peut mémoriser des instances, il faut


utiliser des pointeurs (de la classe mère, qui peuvent pointer sur des instances
de toutes les classes filles).

▶ Les instances de produits étant manipulées par pointeur et allouées


dynamiquement, il faut définir un destructeur (et un constructeur par recopie,
et un opérateur d’affectation, règle des 3).
26
Polymorphisme – Solution utilisant le polymorphisme
Remarque sur les méthodes : Constat :
La méthode produit::afficher n’a pas été
définie virtual.

Conséquence ?
Lors de l’exécution d’afficher, les produits
perissables n’affichent pas leur date limite de
vente.
⇒La méthode de produit est appelée et non sa
redéfinition dans produitPerissable.

Attention ! Donc…
On déclarera toujours virtual les méthodes
pouvant être redéfinies et destinées à être
utilisées par polymorphisme. 27
Polymorphisme et Destructeur

Quand une instance de stock est détruite, le contenu de l’instance est


automatiquement détruit: le vector et les pointeurs qui sont dans ce vector…
pas les objets pointés par ces pointeurs.

Il faut donc les détruire explicitement. C’est le défaut d’utiliser des pointeurs
(plutôt que des instances).

Tous les produits du stock sont détruits, le


destructeur est appelé sur chacun de ces
produits…

28
Polymorphisme et Destructeur
Que se passerait-il si des sous-classes de produit définissaient un destructeur?

▶ Le destructeur est (presque) une méthode comme les autres.


▶ Il est donc, par défaut, à liaison statique.
▶ Un élément de_prod est de type produit *, c’est donc le destructeur de
produit qui est appelé, même si l’objet détruit est une instance d’une sous-
classe.

29
Polymorphisme et Destructeur
Attention.
Ne pas déclarer de destructeur dans une classe revient à déclarer un
destructeur qui «ne fait rien»…et qui est à liaison statique.

Dans produit le destructeur «ne fait rien», mais le déclarer virtual permet de le
redéfinir dans les sous-classes et permet l’appel des destructeurs des sous-
classes par polymorphisme.
30
Détermination du type de la sous classe
On suppose que la classe produitPerissable dispose d’une méthode accesseur à
l’attribut «date de peremption», date const & peremption() const.

31
Détermination du type de la sous classe
Besoin: On veut écrire dans stock une méthode d’affichage de toutes les dates
de péremption.

Est-ce une bonne solution ?


▶ On veut afficher les dates de péremption uniquement pour les
produitPerissable.
▶ Un élément p de _prod est un produit * et peremption() n’est pas définie
dans produit. ⇒ Erreur de compilation.
Comment savoir si un produit * pointe sur un produitPerissable? 32
Détermination du type de la sous classe

Définition (dynamic_cast):

dynamic_cast est un opérateur de conversion qui permet de convertir un


pointeur (une référence) sur une super-classe vers un pointeur (une référence)
sur une de ses sous-classes.

▶ Dans le cas d’une conversion de pointeur, si la conversion n’est pas


possible, retourne nullptr.

▶ Dans le cas d’une conversion de référence, si la conversion n’est pas


possible, lève une exception std::bad_cast.
33
Détermination du type de la sous classe

Corriger le code :

34
Détermination du type de la sous classe

Propriétés de dynamic_cast :

▶ Ne jamais utiliser dynamic_cast sur un pointeur valant nullptr.

▶ Uniquement pour convertir vers une sous-classe.

▶ Uniquement si la classe du pointeur (ou référence) contient au moins une


méthode virtuelle.

▶ A un coût à l’exécution, mais permet de tester et de contrôler.

35
Problème de la classe stock

▶ La classe stock ne respecte pas la règle des 3.

Un destructeur a été défini, mais pas de constructeur par recopie (ni


d’opérateur d’affectation).

Que se passe-t-il si une copie de stock est faite?

Une catastrophe!

Comment ajouter des produits au stock?


De façon robuste, simple, générique.
36
Ajout de produit au stock – solution 1

Erreur de compilation:
cannot declare parameter ‘p’ to be of abstract type ‘produit’

Lors d’un appel à ajouterproduit1 avec un produitPerissable le constructeur par


recopie de produit est appelé et instancie donc un produit copie du
produitPerissable.

37
Ajout de produit au stock – solution 2

Erreur à l’exécution:

Ce qui est rajouté au std::vector est un pointeur sur une variable locale.

On veut donc ajouter au vector un pointeur sur un objet qui n’est pas détruit à
la fin de l’exécution de la méthode.

38
Ajout de produit au stock – Solution 3

Le seul moyen est de créer dynamiquement les produits dans une méthode de
stock, de les détruire dans une méthode de stock.

 Plusieurs méthodes d’ajout.


39
Ajout de produit au stock

Cette solution est correcte mais…

▶ Nécessite de connaître les sous-classes de produit.


▶ Nouvelle sous-classe de produit → rajouter une méthode à stock.

A retenir :
La structure de données utilise le polymorphisme (std::vector<produit*>) mais
pas les méthodes d’ajout.

40
Ajout de produit au stock

▶ On voudrait une seule méthode d’ajout → prenant comme paramètre un


produit (passé par référence ou pointeur).

▶ On voudrait appeler un constructeur pour créer un nouvel objet, copie de


l’objet passé en paramètre →le constructeur par recopie de la classe dont
l’objet est réellement instance, pas produit.

On aurait donc besoin d’un constructeur virtuel.

Mais, Un constructeur ne peut être virtual.


41
Constructeur virtuel

Un «constructeur virtuel» est une façon de construire un nouvel objet, qui est
instance de la même classe qu’un objet existant (par exemple
référence/pointeur de super-classe).

42
Constructeur virtuel

Une seule méthode d’ajout, code robuste et réutilisable (d’autres sous-classes


de produit peuvent être utilisées sans modifier stock).
43
TD

44

Vous aimerez peut-être aussi