Vous êtes sur la page 1sur 49

PROGRAMMATION

ORIENTÉE OBJET:
HÉRITAGE
LAWSON Latevi Sena
PLAN
○  Introduction
○  Les différents spécificateurs
○  Héritage simple
○  Utilisation des membres de la classe de base
dans une classe dérivée
○  Redéfinition des données membres
○  Redéfinition et surdéfinition de fonctions
membres
○  Héritage multiple
○  Classes virtuelles
○  Fonctions virtuelles
○  Classes abstraites

INTRODUCTION

●  Le concept d’héritage constitue l’un des fondements


de la P.O.O.
○ En particulier, il permet la réutilisation de composants
logiciels (en l’occurrence, des classes).
○ En effet, il autorise la définition d’une nouvelle classe, dite
« dérivée », à partir d’une classe existante dite « de base
».
○ La classe dérivée « héritera » des « potentialités » de la
classe de base, tout en lui ajoutant de nouvelles
fonctionnalités.
●  Le langage C++ autorise l’héritage multiple, grâce
auquel une classe donnée peut dériver de plusieurs
autres classes de base.
HÉRITAGE AVEC SPÉCIFICATEUR PUBLIC
●  Syntaxe:
class Derivee : public Base{ . . . } ;

●  Les caractéristiques suivantes sont observées:


○ Les membres publics de la classe Base sont accessibles
à la fois aux fonctions membres et aux fonctions amies de
la classe Dérivée ainsi qu’aux utilisateurs de la classe
Dérivée.
○ Les membres protégés de la classe Base sont accessibles
aux fonctions membres et aux fonctions amies de la
classe Dérivée mais pas aux utilisateurs de cette classe.
HÉRITAGE AVEC SPÉCIFICATEUR PUBLIC

●  Caractéristiques (suite)
○  Les membres privés de la classe Base sont
inaccessibles à la fois aux fonctions membres et aux
fonctions amies de la classe Derivee ainsi qu’aux
utilisateurs de cette classe Derivee.
○ En cas d’une nouvelle dérivation, tous les membres
de la classe Base conservent dans la classe Derivee
le statut qu’ils avaient dans la classe Base.
#include <iostream>
HÉRITAGE AVEC SPÉCIFICATEUR PUBLIC
using namespace std;

class BaseClass {
public:
int public_data;
protected:
int protected_data; int main() {
private: DerivedClass d;
int private_data; d.public_data = 10;
}; d.printData();
return 0;
class DerivedClass : public BaseClass { }
public:
void printData() {
cout << public_data << endl;
cout << protected_data << endl;
// cout << private_data << endl; // Error,
private_data is not accessible
}
};
HÉRITAGE AVEC SPÉCIFICATEUR PUBLIC
Tableau récapitulatif des caractéristiques
HÉRITAGE AVEC SPÉCIFICATEUR PRIVATE
●  Syntaxe :
class Derivee : private Base{ . . . } ;

●  Les caractéristiques suivantes sont observées:


○  La classe Derivee peut avoir accès aux membres
publics et protégés de la classe Base, tandis qu’on
interdit à un utilisateur de la classe Derivee l’accès aux
membres publics de la classe Base
■ cette technique limite l’intérêt de l’héritage
HÉRITAGE
#include <iostream>
using namespace std;
AVEC SPÉCIFICATEUR PRIVATE
class BaseClass {
public:
int public_data; int main() {
protected: DerivedClass d;
int protected_data; // d.public_data = 10; // Error, public_data
private: is not accessible
int private_data; d.printData();
}; return 0;
}
class DerivedClass : private BaseClass {
public:
void printData() {
cout << public_data << endl;
cout << protected_data << endl;
// cout << private_data << endl; // Error,
private_data is not accessible
}
};
HÉRITAGE AVEC SPÉCIFICATEUR PRIVATE
HÉRITAGE AVEC SPÉCIFICATEUR PROTECTED

 Syntaxe :
class Derivee : protected Base{ . . . } ;

● La dérivation protégée se trouve entre la dérivation de spécificateur public et celle de spécificateur private.
● l'héritage avec le spécificateur protected signifie que les membres publics et protégés de la classe de
base sont hérités comme des membres protégés dans la classe dérivée.
● Les membres privés de la classe de base ne sont pas directement accessibles dans la classe dérivée.
#include <iostream>
using namespace std;
HÉRITAGE AVEC SPÉCIFICATEUR PROTECTED
class BaseClass {
public:
int public_data;
protected:
int protected_data;
private: int main() {
int private_data; DerivedClass d;
}; // d.public_data = 10; // Error,
public_data is not accessible
class DerivedClass : protected BaseClass { d.printData();
public: return 0;
void printData() { }
cout << public_data << endl;
cout << protected_data << endl;
// cout << private_data << endl; // Error,
private_data is not accessible
}
};
HÉRITAGE AVEC SPÉCIFICATEUR PROTECTED
HÉRITAGE SIMPLE
On parle de l’héritage simple lorsque la

classe Dérivée a une seule classe parente


HÉRITAGE SIMPLE
 Exemple 1 : Considérons la première classe point définie dans la déclaration :
class point
{
/* déclaration des membres privés */
int x ;
int y ;
/* déclaration des membres publics */
public :
void initialise (int, int) ;
void deplace (int, int) ;
void affiche () ;
};
void point::initialise (int abs, int ord)
{
x = abs ;
y = ord ;
}
void point::deplace (int dx, int dy)
{
x = x + dx ;
y = y + dy ;
}
void point::affiche ()
{
cout << "Je suis en " << x << " " << y << "\n" ;
}
HÉRITAGE SIMPLE
 Supposons que nous ayons besoin de définir un nouveau type classe nommé pointcol, destiné à manipuler des
points colorés dans un plan, comme une classe dérivée de point.

class pointcol : public point // pointcol dérive de point {


short couleur ;
public :
void colore (short cl)
{ couleur = cl ; }
};

 La classe pointcol ainsi définie, nous pouvons déclarer des objets de type pointcol de manière usuelle :
 pointcol p, q ;
 Chaque objet de type pointcol peut alors faire appel :
 aux méthodes publiques de pointcol (ici colore) ;
 aux méthodes publiques de la classe de base point (ici initialise, deplace et affiche).
HÉRITAGE SIMPLE
 Exemple de fonction main
main() {
pointcol p ;
p.initialise (10,20) ;
p.colore (5) ;
p.affiche () ;
p.deplace (2,4) ;
p.affiche () ;
}
UTILISATION DES MEMBRES DE LA CLASSE BASE DANS LA CLASSE DÉRIVÉE

En C++, les membres de la classe de base peuvent être utilisés dans la classe dérivée. Cependant, l'accès à ces membres
dépend du spécificateur de visibilité utilisé lors de l'héritage.

Lorsque la classe dérivée hérite de la classe de base avec le spécificateur public, les membres publics et protégés de la
classe de base peuvent être directement accessibles depuis la classe dérivée.

Lorsque la classe dérivée hérite de la classe de base avec le spécificateur private, les membres publics et protégés de la
classe de base ne peuvent être accédés que par les fonctions membres de la classe de base.

Lorsque la classe dérivée hérite de la classe de base avec le spécificateur protected, les membres publics et protégés de la
classe de base peuvent être accédés par les fonctions membres de la classe dérivée, mais ne peuvent pas être accédés
directement.
UTILISATION DES MEMBRES DE LA CLASSE BASE DANS LA CLASSE DÉRIVÉE

class DerivedClass : public


BaseClass {
#include <iostream> public:
using namespace std; void useBaseData() {
public_data = 10;
class BaseClass { printData();
public: }
int public_data; };
void printData() {
cout << public_data << endl; int main() {
} DerivedClass d;
}; d.useBaseData();
return 0;
}
UTILISATION DES MEMBRES DE LA CLASSE BASE DANS LA CLASSE DÉRIVÉE

●  La classe pointcol telle que nous l’avons


définie présente des lacunes.
●  Par exemple, lorsque nous appelons affiche
pour un objet de type pointcol, nous
n’obtenons aucune information sur sa couleur.
●  Une façon d’améliorer cette situation
consiste à écrire une nouvelle fonction
membre publique de pointcol, censée afficher
à la fois les coordonnées et la couleur.
UTILISATION DES MEMBRES DE LA CLASSE BASE DANS LA CLASSE DÉRIVÉE

○  On pourrait penser écrire :


void affichec (){
cout << "Je suis en " << x << " " << y << "\n" ;
cout << " et ma couleur est : " << couleur
<< "\n" ; }
■ Mauvais!!
○  Il faut plutôt écrire :
void pointcol::affichec (){
affiche () ;
cout << " et ma couleur est : " << couleur <<
"\n" ; }
UTILISATION DES MEMBRES DE LA CLASSE BASE DANS LA CLASSE DÉRIVÉE
Exemple
class pointcol : public point
{ short couleur ;
public :
void colore (short cl)
{ couleur = cl ; }
void affichec () ;
void initialisec (int, int, short) ;
};
void pointcol::affichec ()
{ affiche () ;
cout << " et ma couleur est : " << couleur << "\n" ;
}
void pointcol::initialisec (int abs, int ord, short cl)
{ initialise (abs, ord) ;
couleur = cl ;
}
main()
{
pointcol p ;
p.initialisec (10,20, 5) ; p.affichec () ; p.affiche () ;
p.deplace (2,4) ; p.affichec () ;
p.colore (2) ; p.affichec () ;
}
REDÉFINITION DES MEMBRES D' UNE CLASSE DÉRIVÉE

En C++, la redéfinition des membres d'une classe dérivée consiste à définir une nouvelle
version d'un membre hérité d'une classe de base dans la classe dérivée. Cela permet de
personnaliser le comportement du membre dans la classe dérivée en fonction de ses
besoins spécifiques. La redéfinition est effectuée en utilisant le même nom de membre que
dans la classe de base, avec une nouvelle implémentation.

Il est important de noter que la redéfinition n'affecte pas la définition originale dans la
classe de base. Les objets de la classe de base continuent d'utiliser la définition originale,
tandis que les objets de la classe dérivée utilisent la définition redéfinie. La redéfinition
peut également inclure un appel à la version originale du membre en utilisant la syntaxe
suivante : ClassName::memberName(arguments).
REDÉFINITION
#include <iostream>
DES MEMBRES D' UNE CLASSE DÉRIVÉE
using namespace std;

class Shape {
public:
virtual void draw() { int main() {
cout << "Dessin d'une forme Shape *shape = new Shape();
générale" << endl; Circle *circle = new Circle();
}
}; shape->draw();
circle->draw();
class Circle : public Shape {
public: return 0;
void draw() { }
cout << "Dessin d'un cercle" << endl;
}
};
REDÉFINITION DES MEMBRES D' UNE CLASSE DÉRIVÉE

 Redéfinition
des fonctions membres
d’une classe dérivée
 Dansle dernier exemple de classe pointcol,
nous disposions à la fois :
🞆 dans point, d’une fonction membre nommée affiche ;
🞆 dans pointcol, d’une fonction membre nommée affichec.

 Or ces deux méthodes font le même travail, à


savoir afficher les valeurs des données de leur classe.
Dans ces conditions, on pourrait souhaiter leur donner
le même nom.
REDÉFINITION DES MEMBRES D' UNE CLASSE DÉRIVÉE

 Revoyons l’exemple précédent


class pointcol : public point
{
short couleur ;
public :
void colore (short cl) { couleur = cl ; }
void affiche () ; // redéfinition de affiche de point
void initialise (int, int, short) ; // redéfinition de initialise de point } ;
void pointcol::affiche ()
{
point::affiche () ; // appel de affiche de la classe point
cout << " et le code de ma couleur est : " << couleur << "\n" ; }
void pointcol::initialise (int abs, int ord, short cl)
{
point::initialise (abs, ord) ; // appel de initialise de la classe point couleur = cl ;
}
REDÉFINITION DES MEMBRES D' UNE CLASSE DÉRIVÉE

main()
{
pointcol p ;
p.initialise (10,20,5) ;
p.affiche () ;
p.point::affiche () ; // pour forcer l’appel de affiche de point
p.deplace (2,4) ;
p.affiche () ;
p.colore (2) ;
p.affiche () ;
}
REDÉFINITION DES MEMBRES D' UNE CLASSE DÉRIVÉE

 Ce que nous avons dit à propos de la redéfinition des fonctions membres s’applique tout aussi
bien aux membres données.

Si une classe A est définie ainsi :


class A
{ .....
int a ;
char b ;
.....
};
une classe B dérivée de A pourra, par exemple, définir un autre membre donnée nommé a
comme suit:
class B : public A
{ int a ;
.....
};
REDEFINITION ET SURDEFINITION.
La redéfinition se produit lorsqu'une classe dérivée définit une méthode qui a déjà été définie dans la classe de base. Cela
peut être utilisé pour personnaliser le comportement d'une méthode dans une classe dérivée en fonction de ses besoins
spécifiques.

La surdéfinition, également appelée surcharge, se produit lorsqu'une classe définit plusieurs méthodes portant le même
nom, mais avec des signatures de méthode différentes. Cela permet à une classe d'avoir plusieurs méthodes portant le
même nom, mais avec des fonctionnalités différentes en fonction des arguments différents fournis.
REDEFINITION ET SURDEFINITION.
La redéfinition et la surdéfinition sont des concepts clés en programmation orientée
objet.

La redéfinition est un mécanisme qui permet à une classe dérivée de redéfinir le


comportement d'une méthode héritée d'une classe de base. En utilisant la redéfinition,
une classe dérivée peut modifier le comportement d'une méthode héritée sans changer
son signature (nom et types d'arguments).

La surdéfinition est un mécanisme qui permet à une classe dérivée d'ajouter de nouveaux
comportements à une méthode héritée sans la redéfinir. La surdéfinition est réalisée en
déclarant une nouvelle méthode avec le même nom que la méthode héritée, mais avec
des arguments différents.
REDEFINITION ET SURDEFINITION.
#include <iostream>

class Vector {
public:
int main() {
int x, y;
Vector a(1, 2), b(3, 4);
Vector c = a + b;
Vector(int x = 0, int y = 0) : x(x), y(y)
{}
std::cout << c.x << " " << c.y << std::endl;
return 0;
Vector operator+(const Vector &v)
}
const {
return Vector(x + v.x, y + v.y);
}
};
REDEFINITION ET SURDEFINITION.
 Il va de soi que lorsqu’une fonction est redéfinie dans une
classe dérivée, elle masque une fonction de même
signature de la classe de base.
 Mais les choses ne sont pas aussi simples dans le cas de
l’héritage.
 Lorsqu’une fonction membre est définie dans une classe,
elle masque toutes les fonctions membres de même nom
de la classe de base (et des classes ascendantes).
 Autrement dit, la recherche d’une fonction (surdéfinie ou non)
se fait dans une seule portée, soit celle de la classe
concernée, soit celle de la classe de base.
REDEFINITION ET SURDEFINITION.
Exemple 1 :
class A
{
public :
void f(int n) {cout<<« 1"<<endl; }
void f(char c) {cout<<« 2"<<endl; } // f est surdéfinie dans A } ;

class B : public A
{
public :
void f(float x) {cout<<"3"<<endl; } // on ajoute une troisième définition dans B } ;
main()
{
int n =2; char c='A' ; A a ; B b ;
a.f(n) ; // appelle A::f(int) (règles habituelles)
a.f(c) ; // appelle A::f(char) (règles habituelles)
b.f(n) ; // appelle B::f(float) (alors que peut-être A:f(int) conviendrait) b.f(c) ; // appelle B::f(float) (alors que peut-être
A:f(char) conviendrait)
}
REDEFINITION ET SURDEFINITION.
Exemple 2 :
class A
{
public :
void f(int n) { ..... } // f est surdéfinie void f(char
c) { ..... } // dans A
};
class B : public A
{
public :
void f(int n) { ..... } // on redéfinit f(int) dans B } ;
main()
{
int n=8 ; char c ='A'; B b ;
b.f(n) ; // appelle B::f(int)
b.f(c) ; // appelle B::f(int)
}
REDEFINITION ET SURDEFINITION.
Exemple 3 :
class A //Solution correcte main()
{
public : {
void f(int n) { ..... } int n ; char c ; B b ; b.A::f(n) ;
void f(char c) { ..... }
}; b.A::f(c) ;
class B : public A }
{
public :
void f(int, int) { ..... }
};
main()
{
int n=4 ; char c ='D'; B b ; b.f(n) ; // erreur de compilation b.f(c) ; // erreur de compilation
}
HÉRITAGE MULTIPLE
HÉRITAGE MULTIPLE 2
class point class coul
 Exemple
{{
int x, y ; short couleur ; public : public :
point (...) {...} coul (...) {...} ~point () {...} ~coul ()
{...} affiche () {...} affiche () {...} } ; } ;
Nous pouvons définir une classe pointcoul héritant de ces deux classes en
la déclarant ainsi :
class pointcoul : public point, public coul
{ ... } ;
HÉRITAGE MULTIPLE 3
#include <iostream>
using namespace std ;
class point
{ int x, y ;
public :
point (int abs, int ord)
{ cout << "++ Constr. point \n" ; x=abs ; y=ord ; }
~point () { cout << "-- Destr. point \n" ; }
void affiche ()
{ cout << "Coordonnees : " << x << " " << y << "\n" ; }
};
class coul
{ short couleur ;
public :
coul (int cl)
{ cout << "++ Constr. coul \n" ; couleur = cl ; }
~coul () { cout << "-- Destr. coul \n" ; }
void affiche ()
{ cout << "Couleur : " << couleur << "\n" ; }
};
HÉRITAGE MULTIPLE 4
class pointcoul : public point, public coul
{ public :
pointcoul (int, int, int) ;
~pointcoul () { cout << "---- Destr. pointcoul \n" ; }
void affiche ()
{ point::affiche () ; coul::affiche () ;
}
};
pointcoul::pointcoul (int abs, int ord, int cl) : point (abs, ord), coul (cl)
{ cout << "++++ Constr. pointcoul \n" ;
}
main()
{ pointcoul p(3,9,2) ;
cout << "------------\n" ;
p.affiche () ; // appel de affiche de pointcoul
cout << "------------\n" ;
p.point::affiche () ; // on force l’appel de affiche de point cout << "------------\n" ;
p.coul::affiche () ; // on force l’appel de affiche de coul cout << "------------\n" ;
}
CLASSE VIRTUELLE

La figure ci-dessus peut se traduire à travers


les déclarations suivantes :
class A
{ .....
int x, y ;
};
class B : public A {.....} ;
class C : public A {.....} ;
class D : public B, public C
{ .....
};
CLASSE VIRTUELLE
 D hérite deux fois de A. Dans ces conditions, les membres de
A (fonctions ou données) apparaissent deux fois dans D
 Cependant, on ne souhaitera pas cette duplication des
données.
 En fait, vous pouvez demander à C++ de n’incorporer qu’une
seule fois les membres de A dans la classe D.
 Pour cela, il vous faut préciser, dans les déclarations des classes B
et C (attention, pas dans celle de D !) que la classe A est « virtuelle
» (mot-clé virtual) 🞆 class B : public virtual A {.....} ;
🞆 class C : public virtual A {.....} ;
🞆 class D : public B, public C {.....} ;
CLASSE VIRTUELLE
 Notezbien que virtual apparaît ici dans les
déclarations de B et de C.
 En effet, définir A comme « virtuelle » dans la déclaration
de B signifie que A ne devra être introduite qu’une seule
fois dans les descendants éventuels de C.
 Autrement dit, cette déclaration n’a guère d’effet sur les
classes B et C elles-mêmes (si ce n’est une information
« cachée » mise en place par le compilateur pour
marquer A comme virtuelle au sein de B et C).
🞆 Avec ou sans le mot virtual, les classes B et C, se comportent
de la même manière tant qu’elles n’ont pas de descendants.
FONCTION VIRTUELLE

 Exemple
#include <iostream>
using namespace std;
class A
{
public :
void f(int n) {cout<<"f de A"<<endl;}
};

class B : public A
{
public :
void f(int n) {cout<<"f de B"<<endl;}
};

main()
{
int x ;
A * a;
cin>>x;
if (x==1) a=new A;
else a=new B;
a->f(2); // Quelle est la méthode appelée?
}//Conséquence de la ligature statique i.e fonction déterminée au moment de la compilation.
FONCTION VIRTUELLE
 Ainsi de façon générale, on peut avoir besoin de déclarer
explicitement une méthode comme polymorphe. Ceci se
fait :
 dans la déclaration de la classe de base, en préfixant la
méthode avec le mot clé virtual.  On parle alors de méthode
virtuelle.
//Solution de l’exemple précédent
class A
{
public :
virtual void f(int n) {cout<<"f de A"<<endl;}//f doit être redéfinie dans les classes filles } ;
Ici, il y aura une ligature dynamique. C’est-à-dire que la fonction f qui sera appelée, ne sera déterminée qu’au
moment de l’exécution et tiendra donc compte du vrai type de l’objet sur lequel elle est appelée.
FONCTION VIRTUELLE
 Ainsi, si une méthode d’une classe de base
est virtuelle, alors, en cas d’appel de cette
méthode sur un objet d’une classe dérivée,
 Sila classe dérivée a implémenté la
méthode, c’est celle-ci qui sera exécutée.
 Si la classe dérivée n’a pas implémenté la
méthode, c’est la méthode de la classe de
base qui sera exécutée.
FONCTION VIRTUELLE
 Quelques règles
 Peuvent être virtual
🞆 Les fonctions membres non statiques.
🞆 Les destructeurs.
 Lorsqu'une classe définit une méthode virtuelle, le destructeur
(s'il est défini) doit être obligatoirement virtuel
◉ sinonon risque de n'appeler que le destructeur de la classe mère
alors qu'il s'agit d'un objet de la classe fille.
 Ne peuvent pas être virtual
🞆 Les données membres.
🞆 Les constructeurs.
 Un constructeur ne peut pas être virtuel et ne peut pas appeler
de méthode virtuelle.
CLASSE ABSTRAITE
 Une classe abstraite n'existe que pour être héritée.
 Une classe est dite abstraite si elle contient au
moins une fonction virtuelle pure.
 Ainsi pour rendre par exemple la classe A de l’exemple
précédent abstraite, on aura :

class A
{
public :
virtual void f(int n)=0;//f est une fonction virtuelle pure. Ainsi, f doit être définie //dans les classes
filles
};
CLASSE ABSTRAITE

 Une classe qui contient une méthode


virtuelle pure, ne peut être instanciée car
elle ne contient pas la définition des
fonctions virtuelles pures.
TD
Exercice
 Créer une classe moniteur comprenant :
 les données membres : type (chaîne de caractères), couleurs (long), x_reso (int) et y_reso (int)
 un constructeur initialisant les données membres
 une fonction montrer_moniteur qui affiche les informations sur le moniteur  Créer une classe carte_mere
comprenant :
 les données membres : processeur (int), vitesse (int) et ram (int)  un constructeur
initialisant les données membres
 une fonction montrer_carte qui affiche les informations sur la carte.

 Créer une classe ordinateur dérivant publiquement des classes moniteur et carte_mere et qui contient :
 les données membres : nom (chaînes de caractères), hd (int), lecteur (float)
 unconstructeur initialisant les données membres
 unefonction montrer_ordinateur qui affiche les informations sur l’ordinateur et appelle les fonctions
montrer_moniteur et montrer_carte.
 Créer un programme principal qui crée un objet ordinateur et qui affiche les informations sur cet objet.
 Remplacer les fonctions montrer_moniteur, montrer-carte et montrer_ordinateur par 3 fonctions appelées montrer. Faire
les modifications nécessaires au bon fonctionnement du programme.

Vous aimerez peut-être aussi