Vous êtes sur la page 1sur 47

L’héritage en C++

Héritage simple
Plan du chapitre
▪ Présentation de concept de l’héritage simple
▪ Mise en œuvre de l’héritage
▪ Utilisation dans une classe dérivée des membres de la classe de base
▪ Redéfinition des fonctions membres
▪ Appel des constructeurs et des destructeurs
▪ Contrôle d’accès
▪ Conversion d’un objet dans un objet d’un type de base
Présentation de concept de l’héritage simple
▪ Qu’est ce que l’héritage ?
▪ L’héritage consiste, à partir d’une classe existante A, à définir une nouvelle classe B.
▪ La classe existante A est appelée classe mère, ou classe de base.
▪ La nouvelle classe B est appelée classe fille ou dérivée de la classe A.
▪ On dit que la classe B dérive ou hérite de la classe A.
▪ Une classe fille hérite automatiquement des données et méthodes de sa classe mère sans
avoir à les réécrire.
▪ Une classe mère peut avoir plusieurs classe filles.
▪ Une classe fille peut elle même servir de classe mère pour une autre classe fille.
▪ On parle de l’héritage simple, quand une classe fille hérite d’une seule classe mère.
Présentation de concept de l’héritage simple
▪ Exemple

Classe
véhicule

Classe Classe
DeuxRoues QuatreRoues

Classe
Classe Vélo Classe Moto Classe voiture
Camion
Présentation de concept de l’héritage simple
▪ Utilité de l’héritage
▪ L’héritage permet de réutiliser des classes existantes

▪ L’héritage permet d’adapter des classes existantes à ses propres besoins


▪ L’héritage permet de faire évoluer les classes sans avoir à les réécrire de A à Z.
▪ L’héritage permet de moduler les classes et de les spécialiser au fur et à
mesure des besoins.
▪ L’héritage permet d’éviter de construire des classes de taille trop importante.
Mise en œuvre de l’héritage
▪ La définition d’une classe dérivée d’une classe de base se fait de la manière suivante
(héritage simple):

class nom classe dérivée : modificateur accès nom classe de base


{
Déclaration des membres (données et méthodes) de la classes dérivée
Définitions des méthodes de la classe dérivée
}
Mise en œuvre de l’héritage
Exemple simple sans constructeur ni destructeur

class point{
class point_colore:public point{
int x, y;
Hérite de int couleur;
public:
public:
void initialise(int a, int b){ x=a; y=b; }
void colorer(int c) { couleur=c; }
void deplace(int a, int b){ x=x+a; y=y+b; }
};
void affiche(){
cout<<"Point : "<<x<<" - "<<y<<endl;
}
};
void main(){
point_colore p; // p de type point_colore
- La déclaration ‘class point_colore : public point’ p.initialise(12,27); //méthode de la classe point
spécifie que la classe 'point_colore' est dérivée de p.colorer(7);
la classe de base 'point'. p.affiche(); //méthode de la classe point
- Le mot clé ‘public’ signifie que les membres p.deplace(1,2); // méthode de la classe point
publics de la classe de base seront des membres p.affiche(); // méthode de la classe point
public de la classe dérivée. }
Utilisation dans une classe dérivée des membres de la classe
de base

▪ Après avoir fait appel à la fonction ‘ colorer ’, la fonction ‘affiche’ ne donne aucune information sur la
couleur d'un point.

▪ Ce qui nous conduit donc a créer une fonction ‘ affiche_c ’ membre de ‘point_colore’ pour afficher les
coordonnées x, y et la couleur c.

void affiche_c(){
cout<<"Point : "<<x<<" - "<<y<<endl;
cout<<"En couleur : "<<couleur<<endl;
}

▪ Or, une classe dérivée n'a pas accès aux membres privés de la classe de base. La solution est donc
:
void affiche_c() {
affiche(); //solution accéder à x et y via la méthode affiche qui est public
cout<<" en couleur : "<<couleur<<endl;
}
Utilisation dans une classe dérivée des membres de la classe
de base

▪ De même on peut initialiser x, y et couleur en même temps avec une fonction ‘ initialise_c ’ de la classe
dérivée 'point_colore'.

▪ Le programme de la classe point_c complet est donc :

class point_colore:public point{


int couleur;
int main(){
public:
point_colore p;
void colorer(int c) { couleur=c; }
p.initialise_c(12,27,9);
void affiche_c(){
p.colorer(7);
affiche();
p.affiche();
cout<<"en couleur : "<<couleur<<endl;
p.affiche_c();
}
p.deplace(1,2);
void initialise_c(int a, int b, int c){
p.affiche();
initialise(a,b);
p.affiche_c();
couleur= c;
}
}
};
Redéfinition des fonctions membres
▪ Les méthodes 'affiche' de la classe 'point' et 'affiche_c‘ de la classe 'point_colore' font un travail
analogue. De même pour les fonctions 'initialise' et 'initialise_c'.

▪ En c++, il est possible de redéfinir la fonction 'affiche‘ ou la fonction 'inialise’ pour la classe dérivée.

▪ Exemple

class point_colore:public point {


int couleur; int main(){
public: point_colore p;
void colorer(int c) { couleur=c; } p.affiche();
void affiche(){ p.initialise(12, 27, 9);
point::affiche(); p.affiche();
cout<<"en couleur : "<<couleur<<endl; p.point::affiche();
} p.deplace(10, 20);
void initialise(int a, int b, int c) { p.affiche();
point::initialise(a,b); p.colorer(7);
couleur= c; p.affiche();
} }
};
Contrôle d’accès
2 . les membres protégés d’une classe
▪ Les membres protégés restent inaccessibles à l’utilisateur (comme les membres privés). Mais ils seront
accessibles aux membres d'une éventuelle classe dérivée, tout en restant inaccessibles aux utilisateurs
de cette classe.
▪ Exemple : supposons qu’on a

class point{
protected:
int x, y;
public:
void initialise(int, int);
void deplace(int, int);
void affiche();
};

▪ On peut donc déclarer dans la classe 'point_colore‘ dérivée de la classe 'point' une fonction 'affiche' qui
accède aux membres protégés x et y.
Héritage offre plusieurs avantages

▪ Cela évite d'avoir à réécrire le même code à plusieurs reprises. Un ancêtre peut
transmettre des propriétés à tous ses descendants.
▪ En termes de codage, cela peut vous faire économiser beaucoup de temps et de
travail.
▪ Cela réduit le nombre d'endroits où des bogues pourraient être introduits dans le
programme, ainsi vous réduisez le temps de débogage.
▪ Il permet le polymorphisme
En pratique, l'héritage fonctionne de plusieurs
manières
▪ D'un point de vue descendant, l'héritage orienté objet peut être considéré
comme un moyen de prendre une idée plus générale et de la diviser en idées
plus spécialisées.
▪ Mais l'héritage peut également fonctionner d'un point de vue ascendant, où
vous commencez avec la progéniture et décidez quel type d'ancêtre ils ont en
commun. Que ce soit descendant ou ascendant, le résultat final est un
arrangement similaire de classes.
Contrôle d'accès et héritage

▪ Une classe dérivée peut accéder à tous les membres non privés de sa classe de
base.
▪ Les membres de la classe de base qui ne devraient pas être accessibles aux
fonctions membres des classes dérivées doivent être déclarés privés dans la
classe de base.
▪ Selon le modificateur d'accès utilisé pendant l'héritage, la disponibilité des
membres de la classe Super class dans la sous-classe change. Il peut être
soit private, protected ou public.
Héritage public

▪ Lors de la dérivation public d'une sous-classe d'une classe de base, les


membres publiques de la classe de base deviennent des membres publiques
de la classe dérivée et les membres protégés de la classe de base
deviennent des membres protégés de la classe dérivée.
▪ Les membres privés d'une classe de base ne sont jamais accessibles
directement à partir d'une classe dérivée, mais sont accessibles via des
appels aux membres publiques et protégés de la classe de base.
#include <iostream>
using namespace std;

class Base {
private:
int pvt ;

protected:
int prot ;

public:
int pub ;
Base(){pvt=1; prot=2;pub=3;}

// function to access private member


int getPVT() {
return pvt;
}
};

class PublicDerived : public Base {


public:
// function to access protected member from Base
int getProt() {
return prot;
}

// function to access public member from Base


int getPub() {
return pub;
}
};

int main() {
PublicDerived object1;
//cout << "Private cannot be accessed." << endl;
cout << "Private = " << object1.getPVT() << endl;
cout << "Protected = " << object1.getProt() << endl;
cout << "Public = " << object1.getPub() << endl;
return 0;
}
Héritage protected

▪ Lors de la dérivation protected d'une sous-classe d'une classe de base, les


membres publiques et protégés de la classe de base deviennent des
membres protégés de la classe dérivée.
#include <iostream>
using namespace std;
class Base {
private:

Exemple int pvt ;

protected:
int prot ;

public:
int pub ;
Base(){pvt=1; prot=2;pub=3;}

// function to access private member


int getPVT() {
return pvt;
}
};

class ProtectedDerived : protected Base {


public:
// function to access protected member from Base
int getProt() {
return prot;
}

// function to access public member from Base


int getPub() {
return pub;
}
};

int main() {
ProtectedDerived object1;
cout << "Private cannot be accessed." << endl;
//cout << "Private = " << object1.getPVT() << endl;
cout << "Protected = " << object1.getProt() << endl;
cout << "Public = " << object1.getPub() << endl;
return 0;
}
Héritage private
▪ Lors de la dérivation private d'une sous-classe d'une classe de base, les
membres publiques et protégés de la classe de base deviennent des
membres privés de la classe dérivée.
▪ Syntaxe:
#include <iostream>
using namespace std;

class Base {
private:
int pvt ;

protected:
int prot ;

public:
int pub ;
Base(){pvt=1; prot=2;pub=3;}

// function to access private member


int getPVT() {
return pvt;
}
};

class PrivateDerived : private Base {


public:
// function to access protected member from Base
int getProt() {
return prot;
}

// function to access public member from Base


int getPub() {
return pub;
}
};

int main() {
PrivateDerived object1;
cout << "Private cannot be accessed." << endl;
//cout << "Private = " << object1.getPVT() << endl;
cout << "Protected = " << object1.getProt() << endl;
cout << "Public = " << object1.getPub() << endl;
return 0;
}
Contrôle d’accès
1 . L’héritage privé

▪ Pour que l’utilisateur d’une classe dérivée n’ait pas accès aux membres publics de sa classe de base, il
suffit de remplacer le mot ‘public’ par ‘private’ dans sa déclaration.

▪ Exemple
class point{
int x, y; class point_colore:private point {
public: int couleur;
void initialise (inta,int b) { x=a; y=b; } public:
void deplace (inta,intb) { x=x+a; y=y+b;} void colorer(int c) { couleur=c; }
void affiche() { ……………………… } };
};

▪ Si on a :‘point_colore O(12, 4, 6) ;’ les appels suivants sont rejetés : ‘O.affiche() ;’ ou ‘O.point::affiche();’


‘O.deplace(1,5);’ ou ‘O.point::depace(1,5);’

▪ Cette technique de fermeture d’accès à la classe de base ne sera employée que dans des cas précis,
lorsque par exemple toutes les fonctions utiles de la classe de base sont redéfinies dans la classe
dérivée, et qu’il n’y a aucune raison de laisser l’utilisateur accéder aux anciennes.
#include <iostream>

using namespace std;

//Classe de base
class Vehicule
Autre Exemple {
public:
d’héritage simple int id_vh;
string marque;
int vitesse;
};

// Sous-classe héritant de la classe de base (véhicule)


class Voiture : public Vehicule
{
public:
int id_vt;
};

//fonction main
int main()
{
Voiture v;
// L'objet de la classe enfant a tous les membres de données
v.id_vh = 3;
v.id_vt = 58;
v.marque = "Land";
v.vitesse = 300;
cout << "ID de Voiture est : " << v.id_vh << endl;
cout << "ID de Vehicule est : " << v.id_vt << endl;
cout << "La marque : " << v.marque << endl;
cout << "La vitesse : " << v.vitesse << endl;
return 0;
}
Contrôle d’accès
3 . Tableau récapitulatif sur l’héritage et les modificateurs d’accées

Classe de base Modificateur d’accès Classe dérivées

public public public

public private private

protected public protected

protected private private

private public non accessible

private private non accessible


Appel des constructeurs et des destructeurs
▪ Exemple
▪ Si on a :

class point{ class point_colore:public point{


int x, y; int couleur;
public: public:
point(int, int) ; point_colore(int, int, int) ;
……………………………. ………………………….
………………………… ………………………..
}; };
Appel des constructeurs et des destructeurs
#include <iostream>
using namespace std;

class Base
{
int a;
public:
Base() : a(0) {cout<<"construction objet classe de base"<<endl;}
//Base() : a(0) {} <==> Base(){a(0);}
Base(int A) : a(A) {cout<<"construction objet classe de base"<<endl;}
};
class Derived : public Base{
int b;
public:
Derived() : b(0) {cout<<"construction objet classe Derivee"<<endl;} // appel implicite à Base()
Derived(int i, int j) : Base(i), b(j) {cout<<"construction objet classe Derivee"<<endl;} // appel explicite

};
int main() {
Derived D;
Derived DD(3,4);

return 0;
}
#include <iostream>
using namespace std;
//************Class point******
class point{
int x,y;
public:
point(int abs=0,int ord=0){
cout<<"++ Construct Point:"<<abs<<" "<<ord<<endl;
x=abs; y=ord;
}
~point(){
cout<<"-- Destr Point:"<<x<<" "<<y<<endl;
}
};
//************Class pointcol******
class pointcol: public point{
short couleur;
public:
pointcol(int abs=0,int ord=0, short c=1):point(abs,ord){
cout<<"++ Construct PointCol:"<<abs<<" "<<ord<<" "<<c<<endl;
couleur=c;
}
~pointcol(){
cout<<"-- Destr Couleur:"<<couleur<<endl;
}
};
int main() {
pointcol a(10,15,3);
pointcol b(2,3);
pointcol c(12);
pointcol * adr;
adr=new pointcol(12,25);
delete adr;

return 0;
}
Appel des constructeurs et des destructeurs
▪ Pour créer un objet de la classe ‘point_colore’, il faut tout d’abord créer un objet de la classe ‘point’,
donc faire appel au constructeur de la classe ‘point’, le compléter par ce qui est spécifique a la classe
‘point_colore’ et faire appel au constructeur de la classe ‘point_colore’.

▪ Si on souhaite que le constructeur de la classe ’point_colore’ retransmette au constructeur de la classe


‘point’ les premières informations reçues, on écrira :

Syntaxe : Nom de la classe dérivée (paramètres):nom de la classe mère(paramètres)

Exemple : point_colore(int a, int b, int c):point(a, b)

▪ Ainsi, la déclaration : ‘point_colore(2, 4, 5) ;’ entraînera :


▪ l’appel du constructeur ’point’ qui recevra les valeurs 2 et 4.
▪ l’appel du constructeur ’point_colore’ qui recevra les valeurs 2, 4 et 5.
Appel des constructeurs et des destructeurs

▪ Il est toujours possible de mentionner les valeurs par défaut :

point_colore(int a=0, int b=2, int c=5):point(a,b)

▪ Donc la déclaration ‘point_colore(17) ;’ entraîne :


▪ appel du constructeur ‘point’ avec les valeurs 17 et 0.
▪ appel du constructeur ‘point_colore’ avec les valeurs 17 , 2 et 5.
Appel des constructeurs et des destructeurs
1 . Exercice

Reprendre le programme précédent en ajoutant les constructeurs et les destructeurs


correspondants, tout en affichant les moments de construction et de destruction des objets.
Appel des constructeurs et des destructeurs
2 . D’une manière générale

▪ Si la classe de base ne possède pas de constructeur, aucun problème particulier ne se pose. De même
si elle ne possède pas de destructeur.

▪ En revanche, si la classe dérivée ne possède pas de constructeur, alors que la classe de base en
comporte, le problème sera posé lors de la transmission des informations attendues par le constructeur
de la classe de base. La seule situation acceptable est celle où la classe de base dispose d’un
constructeur sans arguments.

▪ Lors de la transmission d’informations au constructeur de la classe de base, on peut utiliser des


expressions ou des arguments.
▪ Exemple

pointcolore(int a=5, int b=5, int c=4):point(a*2, b*5);


Appel des constructeurs et des destructeurs
3 . Cas du constructeur de recopie

Compléter le programme précédent en ajoutant les constructeurs par recopie.


Conversion d'un objet dérivé dans un objet d'un type
de base

▪ Si on a :

point O1;
point_colore O2;

▪ l'affectation ‘O1=O2 ;’ est juste.


▪ l'affectation ‘O2=O1 ;’ est rejetée (si O2 a des arguments définis par défaut, on aura pas de problème).
Exercice

1. Créer une classe de base Article. Un article possède deux champs privés:
• Nom : string
• Prix : double
Et les opérations publiques :
• getPrix() : pour retourner le prix de l'article
• setPrix(double) : pour changer le prix de l'article
• afficher() : permet d’afficher le prix.
2. Réaliser ensuite une classe ArticleEnSolde, dérivée de la classe Article. Cette sous-classe
comprend une information additionnelle:
• remise : pourcentage de réduction sur le prix d'origine
• setRemise(entier) pour changer la remise.

La classe va redéfinir la méthode getPrix(), afin de tenir compte du solde.


Elle va également redéfinir la méthode afficher(), afin que l'affichage donne également le
pourcentage de remise sur le prix d'origine.
Héritage multiple
Plan du chapitre

▪ Mise en œuvre de l’héritage multiple


▪ Les classes virtuelle
▪ Appel des constructeurs et des destructeurs dans le cas des classes virtuelles
Mise en œuvre de l’héritage multiple

▪ L’exemple suivant met en évidence la notion d’héritage multiple à travers la classe


‘point_colore’ héritant des classes ‘point’ et ‘coul’.
Mise en œuvre de l’héritage multiple
Exemple

class point {
int x,y; class coul {
public: int couleur ;
point(int a, int b){ public :
x=a ; y=b ; coul(int c){
cout<<’’Constructeur de point ’’ ; couleur=c ; cout<<’’Construction de coul ’’ ;
} }
~point() { cout<<’’Destructeur de point ’’ ; } ~coul() { cout<<’’Destruction de coul ’’ ; }
void affiche() { void affiche() { cout<<’’Couleur : ’’ <<couleur<<endl ; }
cout<<’’point ’’ <<x<<’’-‘’<<y ; };
} };

class point_colore:public point, public coul {


public:
point_colore (int a, int b, int c):point (a,b) , coul(c){ void main() {
cout<<’’construction de pointcolore\n’’ ; point_colore o(100, 200, 3);
} o.affiche();
~point_colore() { cout<<’’destruction de pointcolore\n’’ ; } }
void affiche() { point::affiche(); coul::affiche(); }
};
Les classes virtuelle
▪ Si on a :

▪ Donc les déclarations suivantes :


class A {
class B:public A { class C:public A { class D:public B, public C {
int x, y ;
……………… ……………… ………….
……………….
……………… ……………… .…………
……………….
}; }; };
};

▪ Impliquent que la classe ‘D’ hérite deux fois de la classe ‘A’, donc les membres de ‘A’ vont
apparaître deux fois dans ‘D’.
▪ les fonctions membres ne sont pas réellement dupliquées.
▪ les données membres seront effectivement dupliquées.
Les classes virtuelle
▪ Solution
▪ si on veut laisser cette duplication, on utilise :
A::B::x ≠ A::C::x ou B::x ≠ C ::x
▪ si on ne veut pas de cette duplication, on précisera la classe ‘A’ comme classe virtuelle dans les
déclarations des classes ‘B’ et ‘C’.

class A {
class C:public virtual A class D:public B, public C {
int x, y ; class B:public virtual A {
{ ………….
………………. ………………
……………… .…………
………………. ……………… };
……………… }; };
};

Remarques :
- le mot-clé ‘virtual’ apparaît dans la classe ‘B’ et la classe ‘C’.
- le mot-clé ‘virtual’ peut être placé avant ou après le mot-clé ‘public’.
Appel des constructeurs et des destructeurs dans le cas des
classes virtuelles

▪ Si ‘A’ n’est pas déclarée ‘virtual’ dans les classes ‘B’ et ‘C’, les constructeurs seront
appelés dans l’ordre : ‘A1’, ‘B’, ‘A2’, ‘C’ et ‘D’.

▪ Si ‘A’ a été déclarée ‘virtual’ dans ‘B’ et ‘C’, on ne construira qu’un seul objet de type de ‘A’
(et non pas deux objets).

Question : Quels arguments faut-il transmettre alors au constructeur ? Ceux prévus par ‘B’ ou ceux prévus par ‘C’ ?
Appel des constructeurs et des destructeurs dans le cas des
classes virtuelles

▪ Le choix des informations à transmettre au constructeur de ‘A’ se fait, non plus dans ‘B’ ou
‘C’, mais dans ‘D’. Pour ce faire, C++ autorise (uniquement dans ce cas) à spécifier, dans
le constructeur de ‘D’, des informations destinées à ‘A’.

▪ Ainsi, on peut avoir :

D(int a, int b, int c):B(a, b, c), A(a, b)

▪ Bien entendu, il sera inutile de préciser des informations pour ‘A’ au niveau des
constructeurs ‘B’ et ‘C’.

▪ En ce qui concerne l’ordre des appels, le constructeur d’une classe virtuelle est toujours
appelé avant les autres. Dans notre cas, on a l’ordre ‘A’, ‘B’, ‘C’ et ‘D’.
Appel des constructeurs et des destructeurs dans le cas des
classes virtuelles

Exemple :

▪ L’ordre des appels des constructeurs est : B, A, C, D et E.

Vous aimerez peut-être aussi