Vous êtes sur la page 1sur 71

Relations d'héritage entre les classes

Dr SIDIBE A. MA, ENI-ABT


Héritage C++
L'un des concepts les plus importants de la programmation orientée objet est celui de
l'héritage. L'héritage nous permet de définir une classe en fonction d'une autre classe, ce
qui facilite la création et la maintenance d'une application. Cela offre également une
opportunité de réutiliser la fonctionnalité de code et un temps de mise en œuvre rapide. Lors
de la création d'une classe, au lieu d'écrire des membres de données et des fonctions membres
entièrement nouveaux, le programmeur peut indiquer que la nouvelle classe doit hériter des
membres d'une classe existante. Cette classe existante est appelée classe de base, et la
nouvelle classe est appelée classe dérivée. L'idée d'héritage met en œuvre la relation. Par
exemple, mammifère EST-A animal, chien EST-A mammifère donc chien EST-A animal
également et ainsi de suite.
L’idée générale de l’héritage
Un concept spécifique doit avoir les caractéristiques du concept général, mais il peut en
avoir plus. En d'autres termes, un cheval doit d'abord être un animal, puis un cheval. C'est
pourquoi C++ dit qu'une classe dérivée étend sa classe de base. Le terme s'étend ici signifie
que la classe dérivée doit avoir toutes les données membres et fonctions membres définies
dans la classe de base, mais elle peut s'ajouter à la liste. En d'autres termes, la classe
dérivée hérite de toutes les données membres et fonctions membres de la classe de base (à
l'exception des constructeurs, destructeurs et opérateurs d'affectation qui doivent être
redéfinis), et elle peut créer de nouvelles données membres et fonctions membres.

NB: La classe dérivée hérite de tous les membres (à quelques exceptions près) de la
classe de base, et elle peut en ajouter.
L’idée générale de l’héritage
Pour montrer la relation entre les classes en héritage, nous utilisons le langage de modélisation
unifié (UML). UML est un langage qui montre graphiquement la relation entre les classes et les
objets. Les classes sont affichées sous forme de cases rectangulaires en UML. La relation
d'héritage est matérialisée par une ligne se terminant par un triangle creux qui va de la
classe la plus spécifique à une classe plus générale. La figure suivante montre trois relations
d'héritage.

Figure : Diagramme UML pour l'héritage


L’idée générale de l’héritage
En C++, la classe la plus générale est appelée la classe de base et une classe plus
spécifique est appelée la classe dérivée. Une classe plus générale est également connue sous
le nom de superclasse ; une classe plus spécifique est également appelée sous-classe. A
partir d'un diagramme UML, nous pouvons déduire la relation entre l'ensemble des objets
dans un héritage comme dans la figure suivante, qui montre que l'ensemble des chevaux
est plus petit que l'ensemble des animaux.

Figure: Classes et objets dans une hiérarchie d'héritage


L'héritage

L'héritage dans la programmation orientée objet dérive un concept plus spécifique d'un
concept plus général. Par exemple, le concept d'animal en biologie est plus général que le
concept de cheval. Nous pouvons donner la définition d'un animal; nous pouvons ensuite
ajouter à la définition pour créer la définition d'un cheval. Il y a une relation est-un du plus
spécifique au plus général. Tous les chevaux sont des animaux, mais tous les animaux ne
sont pas des chevaux.
Avantage de l'héritage
Certains avantages importants de l'héritage sont les suivants:
1. Réutilisabilité
L'héritage permet au développeur de réutiliser le code existant dans de nombreuses situations. Une
classe peut être créée une fois et elle peut être réutilisée encore et encore pour créer de
nombreuses sous-classes.
2. Gain de temps et d'efforts
l'héritage permet d'économiser beaucoup de temps et d'efforts pour écrire à nouveau les mêmes
classes. la réutilisabilité des classes existantes permet au programme de ne travailler que sur de
nouvelles classes.
3. Augmenter la structure et la fiabilité du programme
Une super classe est déjà compilée et testée correctement. Cette classe peut être utilisée dans une
nouvelle application sans la recompiler. L'utilisation de la classe existante augmente la fiabilité du
programme.
Les Catégories d’Héritage
Il existe deux catégories d'héritage :

1. Héritage unique
Un type d'héritage dans lequel une classe enfant est dérivée d'une classe parent unique est
appelé héritage unique. La classe enfant dans cet héritage hérite de toutes les données membres
et fonctions membres de la classe parent. Il peut également ajouter d'autres fonctionnalités qui
lui sont propres.
Parent

Enfant

Figure: Héritage Simple


Les Catégories d’Héritage
2. Héritage multiple
Un type d'héritage dans lequel une classe enfant est dérivée de plusieurs classes parents
est appelé héritage multiple. La classe enfant dans cet héritage hérite de toutes les
données membres et fonctions membres de toutes les classes parentes. Il peut également
ajouter d'autres fonctionnalités qui lui sont propres.
Parent 1 Parent 2

Enfant

Figure: Héritage Multiple


Le modificateur d'accès Protected

Les données membres private d'une classe ne sont accessibles que dans la classe dans
laquelle elles sont déclarées.
Les données membres public sont accessibles de n'importe où dans le programme.
Le modificateur d'accès protected est différent des modificateurs d'accès private et public.
Il est spécialement utilisé dans l'héritage. Il permet d'accéder à une donnée membre protégée
à partir de toutes les classes dérivées, mais pas de n'importe où ailleurs dans le programme.
Cela signifie que la classe enfant peut accéder à toutes les données protégées membres de sa
classe parent.
La différence entre les modificateurs d'accès

La difference entre les modificateurs d’accèss private, public et protected est comme
suit:

Modificateur Accessible depuis Accessible depuis Accessible depuis


la même classe la sous-classe objet hors classe
d'accès

Private Oui Non Non


Protected Oui Oui Non
Public Oui Oui Oui
Classes de base et dérivées
Une classe peut être dérivée de plusieurs classes, ce qui signifie qu'elle peut hériter des
données et des fonctions de plusieurs classes de base. Pour définir une classe dérivée, nous
utilisons une liste de dérivation de classe pour spécifier la ou les classes de base. Une liste de
dérivation de classe nomme une ou plusieurs classes de base et a la forme
class classe_dérivée : spécificateur d'accès classe_de_base

- spécificateur d'accès peut être public, protected ou private
- classe_de_base est le nom d'une classe précédemment définie. Si le spécificateur d'accès
n'est pas utilisé, il est privé par défaut.
Considérez une classe de base Shape et sa classe dérivée Rectangle comme suit -
#include <iostream> // classe dérivée
using namespace std; class Rectangle : public Shape
// Base class {
class Shape public :
{ int getArea()
protected: {
int width; return (width*height);
int height ; }
public: };
void setWidth(int w) int main()
{ {
width = w; Rectangle Rect ;
} Rect.setWidth(5);
void setHeight(int h) Rect.setHeight(7);
{ // Print the area of the object
height = h; cout <<"Total area: "<<Rect.getArea()<<endl;
} return 0 ;
}; }
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. Ainsi, les
membres de la classe de base qui ne doivent pas être accessibles aux fonctions membres des
classes dérivées doivent être déclarés privés dans la classe de base.
Nous pouvons résumer les différents types d'accès selon - qui peut y accéder de la manière
suivante -
Modificateur Accès depuis la Accès depuis la Accès depuis
même classe sous-classe n'importe où
d'accès

Private Oui Non Non


Protected Oui Oui Non
Public Oui Oui Oui
Une classe dérivée hérite de toutes les méthodes de la classe de base avec les exceptions
suivantes:
 Constructeurs, destructeurs et constructeurs de copie de la classe de base;
 Opérateurs surchargés de la classe de base;
 Les fonctions amies de la base
Types d'héritage
Une classe parent peut être héritée en utilisant un type d'héritage public, protégé ou privé. Le
type d'héritage définit le statut d'accès des membres de la classe parent dans la classe dérivée.
Les différents types d'héritage sont les suivants :
Héritage public
Dans l'héritage public, le statut d'accès des membres de la classe parent dans la classe
dérivée reste le même. Les membres publics de la classe parent deviennent des membres
publics de la classe dérivée. Les membres protégés de la classe parent deviennent des
membres protégés de la classe dérivée. Les membres privés de la classe parent deviennent
des membres privés de la classe dérivée.
Syntaxe
La syntaxe de définition de l'héritage public est la suivante :
class class_enfant : public class_parent
{
Le corps de la classe
}
Les Types d’héritage
Pour créer une classe dérivée à partir d'une classe de base, nous avons trois choix en C+
+ : héritage privé, héritage protégé et héritage public. Pour montrer le type d'héritage que
nous voulons utiliser, nous insérons deux-points après la classe, suivis de l'un des mots-clés
(privé, protégé ou public). La figure suivante montre ces trois types d'héritage dans lesquels
B est la classe de base et D est la classe dérivée.

Figure : les types d’héritage


Les Types d’héritage

Le type d'héritage par défaut est privé. En d'autres termes, si nous ne spécifions pas le
type (public, protégé ou privé), le système suppose que nous voulons un héritage privé.
Puisque l'héritage privé, est rarement utilisé, nous devons définir explicitement le type
d'héritage. Le type d’héritage le plus couramment utilisé est l'héritage public.
Spécification d'une classe dérivée
Le processus de spécification de la classe dérivée est identique à celui de la spécification d'une classe
simple. De plus, la référence du parent est spécifiée avec le nom de la classe dérivée pour hériter des
capacités de la classe parent.
La Syntaxe:
la syntaxe de spécification d'une classe dérivée est la suivante:
class sub_class : spécificateur parent_class
{
le corps de la classe
};
où:
class est c'est le mot clé qui est utilisé pour déclarer une classe.
sub_class est le nom de la classe dérivée.
: il crée une relation entre la classe dérivée et la super classe.
spécificateur - indique le type d'héritage. Il peut être private, public ou protected.
parent_class indique le nom de la classe parent qui est héritée.
Exemple d’Heritage Simple
include <iostream> // classe dérivée
using namespace std; class Rectangle : public Shape
// classe de base {
class Shape public :
{ int getArea()
protected: {
int width; return (width*height);
int height ; }
public: };
void setWidth( int w) int main()
{ {
width = w; Rectangle Rect ;
} Rect.setWidth(5);
void setHeight( int h) Rect.setHeight(7);
{ // affiche l’aire de l’objet.
height = h; cout <<"Total area: "<<Rect.getArea()<<endl;
} return 0 ;
}; }
L’Accès au membre de la classe parent
Un problème important dans l'héritage est l'accessibilité des membres de la classe de base
par les objets de la classe dérivée. C'est ce qu'on appelle l'accessibilité. Les objets de la
classe dérivée peuvent accéder à certains membres de la classe parent.
Accès aux constructeurs de la classe Parent
Les objets de la classe dérivée peuvent accéder à certains constructeurs de la classe mère. Il
utilisera un constructeur approprié de la classe parent si aucun constructeur n'est déclaré dans
la classe dérivée. Les objets de la classe peuvent accéder automatiquement aux constructeurs
de la classe parent si la classe dérivée ne déclare aucun constructeur et que la classe parent a
un constructeur sans paramètre. La classe dérivée peut également accéder aux constructeurs
de la classe parent avec des paramètres en leur transmettant des valeurs.
Accès aux constructeurs de la classe Parent
La syntaxe
La syntaxe d'accès au constructeur de la classe parent dans la classe dérivée est la suivante :
child_con : parent_con(parametres)
{
le corps du constructeur
}
Ou:
child_con est le nom du constructeur de la classe dérivée.
parent_con est le nom du constructeur de la classe parent.
parametres est la liste des paramètres passés au constructeur de la classe parent.
Exemple d’accès aux constructeurs de la classe Parent
#include <iostream>
using namespace std;
class Parent class Enfant:public Parent
{ int main()
{ {
protected: private:
int n; char ch; Enfant obj1, obj2('@',100);
public: public:
Parent() cout<<" Obj1 est défini comme suit:\n" ;
Enfant():Parent()
{ { obj1.show();
n = 0; ch= 'x';
} obj1.display();
}
Parent(int p) Enfant(char c, int m):Parent(m) cout<<" Obj2 est défini comme suit:\n" ;
{ {
n = p; obj2.show();
ch = c;
} } obj2.display();
void show() void display()
{ return 0;
{ }
cout<<"n = "<<n<<endl; cout<<"ch = "<<ch<<endl;
} }
}; };
L’Accès aux Fonctions membres de la classe parent
Les objets de la classe dérivée peuvent accéder à toutes les fonctions membres de la classe
parent qui sont déclarées comme protected ou public.

Exemple
Écrivez une classe Person qui a les attributs id, name et address. il a un constructeur pour
initialiser, une fonction membre pour la saisi des données members, et une fonction membre
pour afficher les données membres.
Créez une autre classe Student qui hérite de la classe Person. Elle a des attributs
supplémentaires comme numéro de matricule et la note. Elle a également une fonction
membre pour saisir et afficher ses données membres.
Exemple d’accès aux constructeurs de la classe Parent
#include <iostream>
#include<string.h>
using namespace std; Person::Person() void Person::Getinfo()
class Person { {
{ id = 0; cout<<"Enter your id :";
protected: name=" "; cin>>id;
int id; address=" "; cout<<"Enter your name:";
string name; } cin>>name;
string address; Person::Person(int i,string Nm,string addr) cout<<"Enter your address:";
public: //:id(i),name(Nm),address(addr) cin>>address;
Person(); { }
Person(int,string,string); id = i; void Person::Showinfo()
Person(const Person&); name=Nm; {
void Getinfo(); address=addr; cout<<"\n your personal information is as follows:\n";
void Showinfo(); } cout<<"id = "<<id<<endl;
}; Person::Person(const Person& Prs) cout<<"Name = "<<name<<endl;
{ cout<<"Address = "<<address<<endl;
id = Prs.id; }
name=Prs.name;
address=Prs.address;
}
Exemple d’accès aux constructeurs de la classe Parent
class Student : public Person
{ void Student::GetEdu()
private: {
int rno,marks; cout<<"Enter your roll no:";
public: cin>>rno;
Student(); cout<<"Enter your marks:";
Student(int,int,int,string,string); cin>>marks;
void GetEdu(); }
void ShowEdu(); void Student::ShowEdu()
}; {
Student::Student() cout<<"\n les informations académiques sont:\n";
{ cout<<"Roll No ="<<rno<<endl;
Person(); cout<<"Marks ="<<marks<<endl;
rno=0; }
marks=0;
}
Student::Student(int num,int m, int i, string Nm, string addr):Person(i,Nm,addr)
{
rno=num;
marks=m;
}
Héritage public

Bien que le type d'héritage par défaut soit l'héritage privé, le plus courant est l'héritage
public. Les deux autres types d'héritage sont rarement utilisés. Dans cette section nous nous
concentrons sur l'héritage public. Certains autres langages orientés objet, comme Java, n'ont
qu'un héritage public.
Héritage public
Exemple 1
Dans cet exemple, nous concevons deux classes, Person et Student, dans lesquelles la classe
Student hérite de la classe Person. Nous savons qu'un étudiant est une personne. Nous
supposons que la classe Person utilise une seule données membre: identity (comme le numéro
de sécurité sociale). Nous supposons également que la classe Student a besoin de deux données
membres: identity et gpa. Cependant, étant donné que la donnée membre identity est déjà
défini dans la classe Person, elle n'a pas besoin d'être défini dans la classe Student en raison de
l'héritage. La figure suivante montre le diagramme de classes UML étendu avec deux
compartiments pour accueillir les membres de données.
Exemple 1 (suite)
La figure suivante montre le diagramme de classes UML étendu avec deux compartiments
pour accueillir les membres de données.
Remarques:
Le type des données membres s'affiche après le nom
du membre, séparé par deux-points.
Les signes moins définissent la visibilité des
données membres comme privées.

Figure: Deux classes dans une relation d'héritage

Sur la base de la figure ci-dessous, nous pouvons voir immédiatement l'avantage de


l'héritage. La classe Student utilise la donnée membre de la classe Person et ajoute une
donnée membre qui lui est propre.
Exemple 2
Dans cet exemple, nous ajoutons des fonctions membres aux deux classes. Nous ignorons les
constructeurs et le destructeur à ce stade car ils ne sont pas hérités ; Nous ajoutons une fonction
getter et une fonction setter à chaque classe. Nous utilisons un diagramme UML plus étendu avec
un autre compartiment définissant les fonctions membres, comme le montre la figure suivante:
Remarques:
Le type des données membres et des fonctions
membre est affiché après le nom du membre,
séparé par deux-points.
Les signes moins définissent la visibilité des
données membres comme privée ;
les signes plus définissent la visibilité des
fonctions membres comme publiques.
Figure: Classes avec à la fois des données membres et des fonctions membres
Exemple 2 (suite)

Figure: Classes avec à la fois des données membres et des fonctions membres

A partir de la figure ci-dessous, nous pouvons voir un autre avantage l’héritage. L'objet de la
classe Student doit définir et obtenir la valeur de identity, mais identity est déjà définie
dans la classe Person et la classe Student n'a pas besoin de la définir ou de l'obtenir.
Exemple 3 : (voir le fichier code source Public_inheritance.cpp)
Program montre l’idée dans un programme a simple program.
Le programme montre comment la classe Student hérite de la classe Person parce qu’un
étudiant est une personne.

Resultat du programme
Analyse du programme

Il y a trois points importants à propos de ce programme :


a) Il y a deux classes dans ce programme. Nous instancions un objet de la classe Person et
nous instancions également un objet de la classe Student.
b) Bien que nous n'ayons pas défini la donnée membre identity pour la classe Student, un
objet de cette classe hérite identity de la classe Person.
c) Nous avons utilisé une identité au lieu d'un nom pour identifier une personne.
Données membres Privées dans l'héritage public

Dans l'héritage public, les données membre privé dans la classe de base sont hérités dans
la classe dérivée, mais elles deviennent inaccessible (parfois appelé caché) dans la classe
dérivée. Dans la classe dérivée, les données membre privé hérités de la classe de base sont
accessible uniquement via leur propres fonctions membre définies dans la classe de base.

NB:
Les membres de données privées hérités deviennent
des membres inaccessibles (cachées) dans la classe
dérivée. elles doivent être accessibles via la classe de
base.
Figure: Données membres privées dans
l'héritage public
Fonctions membres publique dans l'héritage public

Un membre public de la classe de base devient un membre public de la classe dérivée.

NB:
Dans l'héritage public, les fonctions membres
publiques sont héritées en tant que fonctions
membres publiques dans la classe dérivée.

Figure: Fonctions membres publiques dans


l'héritage public
Accès aux Données Membres Privées

La liste suivante montre comment nous accédons aux données membres privées dans chaque
classe (classe de base et classe Dérivée):
 Dans la classe de base, nous pouvons accéder aux données membres privées via les
fonctions membres publiques définies dans cette classe (dans notre exemple il s’agit des
fonctions setId et getId).
 Dans la classe dérivée, nous avons besoin de deux groupes de fonctions membres publiques :
a. Pour accéder aux données membres privées héritées, qui sont cachées, nous utilisons
les fonctions membres publiques définies dans la classe de base (setId et getId).
b. Si nous devons accéder à une donnée membre définie dans la classe dérivée, nous
utilisons les fonctions membres définies dans la classe dérivée (setGPA et getGPA).
Fonctions Portant le même Nom dans Différentes Classes

Dans les deux classes précédentes, Person et Student, nous avons utilisé deux fonctions
membres pour définir les membres de données (setId et setGPA) et deux fonctions membres
pour obtenir les membres de données (getId et getGPA).
Peut-on utiliser deux fonctions portant le même nom, l'une dans la classe de base et l'autre
dans la classe dérivée ?
En d'autres termes, peut-on avoir une fonction nommée set dans la classe de base et une autre
fonction également nommée set dans la classe dérivée ? Autrement dit, peut-on avoir une
fonction nommée get dans la classe de base et une autre fonction également nommée get
dans la classe dérivée ? La réponse aux deux questions est positive, mais nous devons
utiliser deux concepts différents : fonctions surchargées et remplacées (overridden
Fonctions Portant le même Nom dans Différentes Classes

NB: Pour utiliser le même nom pour une fonction dans la classe de base et la classe
dérivée, nous avons besoin de fonctions membres surchargées ou remplacées.
Fonctions membres surchargées
Les fonctions surchargées sont deux fonctions portant le même nom mais avec deux
signatures différentes. Les fonctions surchargées peuvent être utilisées dans la même classe
ou dans des classes différentes sans être confondues les unes avec les autres. On peut avoir
deux fonctions nommées set, l'une dans la classe de base et l'autre dans la classe dérivée, avec
les prototypes suivants :

Comme les signatures sont différentes, nous pouvons utiliser la première fonction set dans la
classe Person et la seconde fonction set dans la classe Student.
Fonctions membres remplacées (Overridden Function)

Les fonctions remplacées sont deux fonctions portant le même nom et qui ont la même
signature. Nous avons remplacé les fonctions membres comme indiqué ci-dessous:
Portée de la classe (Class Scope en Anglais)
Les classes dans la hiérarchie d'héritage public ont également une portée. La classe de
base a sa propre portée de classe et la classe dérivée a la sienne. Cependant, la portée de
la classe dérivée est incluse dans la portée de la classe de base comme le montre la figure
ci-dessous.

Figure: Portée des classes de base et dérivées

Dans la figure ci-dessus, la portée des deux classes montre que chaque nom défini dans
la classe de base est également visible dans la classe dérivée, mais l'inverse n'est pas
vrai.
Portée de la classe (Class Scope en Anglais)

Comment le système peut-il distinguer quelle fonction nous appelons ?


Nous devons nous rappeler que nous n'appelons pas les fonctions membres d'une classe
uniquement par leur nom ; nous laissons l'instance appeler la fonction appropriée. En
d'autres termes, si person et student sont deux objets des classes Person et Student,
respectivement, nous utilisons les deux instructions suivantes :
Comment le système peut-il distinguer quelle fonction nous appelons ?

Scope nous indique comment un compilateur invoque (appelle) une fonction selon les règles
suivantes :
1. Le compilateur essaie de trouver une fonction correspondante (en utilisant des noms et
des paramètres) qui appartient à la classe de l'objet qui a appelé la fonction.
2. Si aucune fonction correspondante n'est trouvée, le compilateur examine les fonctions
héritées de la superclasse.
3. Si aucune correspondance n'est trouvée, la recherche se poursuit jusqu'à ce que la
classe de base soit atteinte.
4. Si aucune correspondance n'est trouvée dans une classe, une erreur de compilation est
émise.
Délégation de devoir

Une fonction membre surchargée ou remplacée dans une classe dérivée peut déléguer une
partie de son opération à une fonction membre dans une classe de niveau supérieur en appelant
la fonction membre correspondante. Cela se fait généralement facilement avec les fonctions
membres de type void ;
Délégation de devoir

Par exemple, la fonction de membre set dans la classe Student peut être conçue pour définir
la donnée membre de la classe Person, identity, et la donnée membres de sa propre classe,
gpa. Cependant, la données membre identity est inaccessible (ou caché) dans la classe
Student et n'est pas accessible par la fonction set de la classe Student, mais la fonction set
de la classe Student peut appeler la fonction set de la classe Person.
De la même manière, nous pouvons concevoir une fonction d'impression print dans la
classe Person qui imprime uniquement la valeur du donnée membre identity. Nous
pouvons également concevoir une fonction d'impression print dans la classe Student pour
appeler la fonction d'impression print dans la classe Person pour effectuer certaines parties
de son travail.
Délégation de devoir (suite)
Ce qui suit montre la définition de la fonction set et de la fonction print avec
délégation.

NB: pour appeler la fonction set ou la fonction print dans la classe de base, nous devons
utiliser l'opérateur de portée de classe, Person :: set(...) ou Person :: print().
Les Membres non Hérités

Il existe cinq fonctions membres qui ne sont pas héritées dans la classe dérivée : le
constructeur par défaut, le constructeur de paramètres, le constructeur de copie, le
destructeur et l'opérateur d'affectation.
Les constructeurs et le destructeur ne sont pas hérités car un objet d'une classe dérivée a
naturellement plus de membres de données qu'une classe de base correspondante.
Le constructeur d'une classe dérivée doit en construire davantage ;
Le destructeur d'une classe dérivée doit en détruire davantage.

NB: Les constructeurs, les destructeurs et les opérateurs d'affectation ne sont pas
hérités. Ils doivent être redéfinis.
Les Membres non hérités

Le constructeur de la classe dérivée ne peut pas initialiser les données membres de la


classe de base car elles sont masquées dans la classe dérivée. De même, le destructeur
d'une classe dérivée ne peut pas supprimer les données membres de la classe de base car
elles sont masquées dans la classe dérivée.
Le problème du constructeur peut être résolu si le constructeur de la classe dérivée appelle le
constructeur de la classe de base lors de son initialisation, puis initialise les données
membres de la classe dérivée. De même, le problème du destructeur peut être résolu si le
destructeur de la classe dérivée supprime d'abord les données membres de la classe
dérivée puis appelle le destructeur de la classe de base, comme le montre la figure suivante:
Les Membres non hérités

Figure : Constructeurs et destructeur en héritage


Les Membres non hérités: Exemple
Exemple:
Le tableau ci-dessous montre le constructeur par défaut, le constructeur de paramètres, le
constructeur de copie et le destructeur pour nos classes Person et Student côte à côte. Notez
que la section de classe dérivée utilise le constructeur de classe de base (invocation). Après
avoir appelé le constructeur de la classe de base, l'initialiseur peut initialiser le membre privé
de la classe dérivée. Le terme st dans l'appel au constructeur de copie fait référence à un objet
Student. Vous pouvez vous demander comment nous pouvons passer cet objet au constructeur
de copie Person, qui a besoin d'un paramètre de type objet Person. L’énigme (puzzle)peut être
résolu si nous savons que chaque fois que nous utilisons un objet de la classe dérivée où un
objet de la classe de base est nécessaire, l'objet est découpé en tranches et les membres de
données appartenant à la classe dérivée sont supprimés. En d'autres termes, l'objet dans l'appel
Person (st) n'est que la partie de la classe Student héritée dela classe Personne.
Les Membres non hérités: Exemple

NB: Les ordres d'activités dans un constructeur et un destructeur sont inversés. Étant donné
que le destructeur est appelé par le système, et non par l'utilisateur, les activités sont
effectuées en arrière-plan, sauf si l'objet utilise des pointeurs ou des fichiers pouvant
nécessiter l'intervention de l'utilisateur.
Les Membres non hérités: Exemple
La figure ci contre montre comment la
classe de base et les objets de la classe
dérivée sont construits à l'aide du
constructeur de paramètres. Dans le cas
de la classe de base (Person), nous
devons initialiser la seule donnée
membre. Dans la classe dérivée
(Student), nous pouvons construire la
partie héritée en appelant le constructeur
de la classe de base, puis en initialisant le
nouveau membre de données. Le
constructeur de la classe de base est un
membre public et est accessible dans la
L'accessibilité de la classe dérivée dans l'héritage public est la suivante:

 La classe dérivée peut accéder aux membres publics de la classe parent.


 La classe dérivée peut accéder aux membres protégés de la classe parent.
 La classe dérivée ne peut pas accéder aux membres privés de la classe parent.

L'accessibilité d'un objet de classe dérivée est la suivante :

 L'objet de la classe dérivée peut accéder aux membres publics de la classe parent.
 L'objet de la classe dérivée ne peut pas accéder aux membres protégés de la classe parent.
 L'objet de la classe dérivée ne peut pas accéder aux membres privés de la classe parent.
L’Accessibilité de la Classe Dérivée et de l’Objet de la Classe Dérivé
dans l’Héritage Public
Classe Parent
Private
Protected
Public

Héritage Public

Private
Protected
Public Objet de la classe derivée

Classe Derivée

La figure ci-dessus montre que les membres privés, protégés et publics de la classe dérivée peuvent accéder
aux membres protégés et publics de la classe parent. Cependant, l'objet de la classe dérivée ne peut
accéder directement qu'aux membres publics des deux classes.
Exemple d’Accessibilité de la Classe Dérivée et de l’Objet de la Classe Dérivé
dans l’Héritage Public
void Enfant::input()
Exercice { cout<<"Entrez a:";
#include <iostream>
cin>>a;
Ecrire un programme qui using namespace std;
cout<<"Entrez b:";
class Parent
cin>>b;
déclare deux classes et définit {
/*
public:
une relation entre elles en cout<<"Entrez c:";
int a;
cin>>c;
protected:
utilisant l'héritage public. int b;
*/
}
private:
void Enfant::output()
int c;
{ cout<<"a = "<<a<<endl;
};
cout<<"b = "<<b<<endl;
class Enfant : public Parent
}
{
int main()
public:
{ Enfant obj;
void input();
obj.input();
void output();
obj.output();
};
return 0;
}
Héritage Protégé
Dans l'héritage protégé, le statut d'accès des membres de la classe parent dans la classe
dérivée est restreint. Les membres publics de la classe parent deviennent des membres
protégés de la classe dérivée. Les membres protégés de la classe parent deviennent des
membres protégés de la classe dérivée. Les membres privés de la classe parent
deviennent des membres privés de la classe dérivée.

Syntaxe
La syntaxe de définition de l'héritage protégé est la suivante :
class class_enfant : protected class_parent
{
Le corps de la classe
}
L'accessibilité de la classe dérivée dans l'héritage protégé est la
suivante:
 La classe dérivée peut accéder aux membres publics de la classe parent.
 La classe dérivée peut accéder aux membres protégés de la classe parent.
 La classe dérivée ne peut pas accéder aux membres privés de la classe parent.

L'accessibilité d'un objet de classe dérivée est la suivante :

 L'objet de la classe dérivée ne peut pas accéder aux membres publics de la classe parent.
 L'objet de la classe dérivée ne peut pas accéder aux membres protégés de la classe parent.
 L'objet de la classe dérivée ne peut pas accéder aux membres privés de la classe parent.
L’Accessibilité de la Classe Dérivée et de l’Objet de la Classe Dérivé
dans l’Héritage Protégé
Classe Parent
Private
Protected
Public

Héritage Protégé

Private
Protected
Public Objet de la classe derivée

Classe Derivée

La figure ci-dessus montre que les membres privés, protégés et publics de la classe dérivée peuvent
accéder aux membres protégés et publics de la classe parent. Cependant, l'objet de la classe dérivée ne
peut accéder directement qu'aux membres publics de la classe dérivée. l'objet de la classe parent ne
peut accéder directement à aucun member de la classe parent.
Exemple d’Accessibilité de la Classe Dérivée et de l’Objet de la Classe Dérivé
Exercice dans l’Héritage Protégé
void Enfant::input()
{ cout<<"Entrez a:";
Ecrire un programme qui #include <iostream>
cin>>a;
using namespace std;
cout<<"Entrez b:";
déclare deux classes et définit class Parent
cin>>b;
{
une relation entre elles en /*
public:
cout<<"Entrez c:";
int a;
utilisant l'héritage protégé. protected:
cin>>c;
*/
int b;
NB: }
private:
void Enfant::output()
La principale différence entre int c;
{ cout<<"a = "<<a<<endl;
};
l'héritage public et l'héritage cout<<"b = "<<b<<endl;
class Enfant : protected Parent
}
{
protégé est que les membres public:
int main()
{ Enfant obj;
publics de la classe parent void input();
obj.input();
void output();
deviennent protégés pour la };
obj.output();
return 0;
class dérivée dans l'héritage }
Héritage Privé

Dans l'héritage protégé, le statut d'accès des membres de la classe parent dans la classe
dérivée est restreint. Les membres publics, protégés et privés de la classe parent
deviennent tous des membres privés de la classe dérivée.

Syntaxe
La syntaxe de définition de l'héritage privé est la suivante :
class class_enfant : private class_parent
{
Le corps de la classe
}
L'accessibilité de la classe dérivée dans l'héritage privé est la suivante:

 La classe dérivée peut accéder aux membres publics de la classe parent.


 La classe dérivée peut accéder aux membres protégés de la classe parent.
 La classe dérivée ne peut pas accéder aux membres privés de la classe parent.

L'accessibilité d'un objet de classe dérivée est la suivante :

 L'objet de la classe dérivée ne peut pas accéder aux membres publics de la classe parent.
 L'objet de la classe dérivée ne peut pas accéder aux membres protégés de la classe parent.
 L'objet de la classe dérivée ne peut pas accéder aux membres privés de la classe parent.
L’Accessibilité de la Classe Dérivée et de l’Objet de la Classe Dérivé
Classe Parent
dans l’Héritage Privé
Private
Protected
Public

Héritage Privé

Private
Protected
Public Objet de la classe derivée

Classe Derivée
La différence principale entre l’héritage public et l’héritage privé est que l’héritage privé est
généralement utilisé pour restreindre l’accès à la classe parent par des classes qui sont dérivées de la
classe enfant. Dans l’héritage privé, tous les membres publics et protégés de la classe parent deviennent
des membres privés dans les objects de la classe dérivé. Ils ne sont pas accessible par les fonctions des
classes dérivées de la classe enfant.
Exemple d’Accessibilité de la Classe Dérivée et de l’Objet de la Classe Dérivé
Exercice dans l’Héritage Privé
void Enfant::input()
{ cout<<"Entrez a:";
Ecrire un programme qui #include <iostream>
cin>>a;
using namespace std;
cout<<"Entrez b:";
déclare deux classes et définit class Parent
cin>>b;
{
une relation entre elles en /*
public:
cout<<"Entrez c:";
int a;
utilisant l'héritage protégé. protected:
cin>>c;
*/
NB: int b;
}
private:
La principale différence entre l'héritage void Enfant::output()
int c;
privé et les autres héritages est que { cout<<"a = "<<a<<endl;
};
cout<<"b = "<<b<<endl;
toutes les données membres de la class Enfant : private Parent
}
{
classe parent deviennent des membres int main()
public:
privés de la classe dérivée. Toute classe { Enfant obj;
void input();
obj.input();
qui hérite de la classe dérivée ne peut void output();
obj.output();
accéder à aucun membre de la classe };
return 0;
dérivée. }
Remplacement de fonction (Function Overriding)
Le processus de déclaration de la fonction membre dans la classe dérivée avec le même nom et la
même signature a dans la classe parent est appelé remplacement de fonction.
Le remplacement de fonction permet à l'utilisateur d'utiliser les mêmes noms pour appeler les
fonctions membres de différentes classes. Lorsqu'une fonction membre est redéfinie dans la classe
dérivée, l'objet de la classe dérivée ne peut pas accéder à la fonction de la classe parent.
Cependant, la fonction de la classe parent est accessible en utilisant la résolution de portée.

Le remplacement de fonction permet de redéfinir une fonction de la classe de base à l'intérieur de


la classe dérivée, qui remplace la fonction de classe de base. Le remplacement de fonction est une
implémentation du polymorphisme d'exécution. Ainsi, il remplace la fonction au moment de
l'exécution du programme.
Exemple de remplacement de fonction
#include <iostream>
using namespace std;
class Parent
{
protected: class Child : public Parent void Child::show() // remplacement de la fonction show()
int n; { {
public: private: Parent::show();
Parent(); char ch; cout<<"ch = "<<ch<<endl;
Parent(int p); public: }
void show(); Child(); int main()
}; Child(char c,int m); {
Parent::Parent() void show(); Child obj1('@',100);
{ }; cout<<" Obj1 est défini comme suit:\n" ;
n = 0; Child::Child():Parent() obj1.show();
} { return 0;
Parent::Parent(int p) ch= 'x'; }
{ n = p; }
} Child::Child(char c,int m):Parent(m)
void Parent::show() {
{ ch = c;
cout<<"n = "<<n<<endl; }
}
Comment fonctionne le programme ci-dessus ?

Le programme ci-dessus déclare deux classes. Les deux classes déclarent une fonction
membre show() pour afficher la valeur des données membres. La classe dérivée remplace la
fonction show(). L'objet de la classe dérivée ne peut pas utiliser cette fonction
directement. L'objet de la classe dérivée appelle la fonction déclarée dans la classe
dérivée. La fonction membre de la classe dérivée appelle ensuite la fonction de la classe
parent avec l'instruction suivante :
Parent::show();
Exercice

Ecrire un programme qui déclare deux classes. La classe parent est appelé Simple qui a
deux membres de données a et b pour stocker deux nombres. Il a également quatre
fonctions membres :
 La fonction add() additionne deux nombres et affiche le résultat.
 La fonction sub() soustrait deux nombres et affiche le résultat.
 La fonction mul() multiplie deux nombres et affiche le résultat.
 La fonction div() divise deux nombres et affiche le résultat.
La classe enfant est Complexe qui remplace les quatre fonctions. Chaque fonction de la
classe enfant vérifie la valeur des données membres. Il appelle la fonction membre
correspondante dans la classe parent si les valeurs sont supérieures à 0. Sinon, il affiche un
message d'erreur.
#include <iostream> void Simple::in() class Complex: public Simple
using namespace std; { cout<<"Entrez a:"; {
class Simple cin>>a; public:
{ cout<<"Entrez b:"; void add();
cin>>b; void sub();
protected:
} void mul();
int a, b;
void Simple::add() void div();
public: };
{
Simple(); void Complex::add()
cout<<"a + b="<<a+b<<endl;
void in(); {
}
void add(); if(a<=0 || b<=0)
void Simple::sub()
void sub(); { cout<<"Valeurs Invalides"<<endl;
void mul(); cout<<"a - b="<<a-b<<endl; else
void div(); Simple::add();
}
}; }
void Simple::mul()
Simple::Simple() void Complex::sub()
{
{ {
cout<<"a * b="<<a*b<<endl;
if(a<=0 || b<=0)
a=0; } cout<<"Valeurs Invalides"<<endl;
b=0; void Simple::div() else
} { Simple::sub();
cout<<"a/b="<<a/b<<endl; }
}
void Complex::mul()
{ if(a<=0 || b<=0)
cout<<"Valeurs Invalides"<<endl;
else
Simple::mul();
}
void Complex::div()
{ if(a<=0 || b<=0)
cout<<"Valeurs Invalides"<<endl;
else
Simple::div();
}
int main(){
Complex obj;
obj.in();
obj.add();
obj.sub();
obj.mul();
obj.div();
return 0;
}
Héritage multiple

Une classe C++ peut hériter des membres de plusieurs classes et voici la syntaxe étendue:
class classe dérivée : accès classe_baseA, accès classe_baseB....
Où l'accès peut être public, protégé ou privé et serait donné pour chaque classe de base et ils
seront séparés par une virgule comme indiqué ci-dessus. Examinons l'exemple suivant:
Exemple d’héritage multiple
#include <iostream> // Base class PaintCost
using namespace std; class PaintCost int main()
// Base class Shape { {
class Shape public: Rectangle Rect ;
{ int getCost(int area) int area;
protected: { Rect.setWidth(5);
int width; return area*70 ; Rect.setHeight(7);
int height; } area = Rect.getArea();
public: }; // Print the area of the object.
void setWidth( int w) // Derived class cout<< "Total area: “
{ class Rectangle : public Shape, public PaintCost << Rect.getArea()
width = w; { << endl;
} public : // Print the total cost of painting
void setHeight( int h) int getArea() cout<<"Total paint cost: $"
{ { <<Rect.getCost(area)
height = h; return (width*height); <<endl;
} } return 0 ;
}; }; }

Vous aimerez peut-être aussi