Chapitre 6 S3 SEIB 2020-2021

Télécharger au format pdf ou txt
Télécharger au format pdf ou txt
Vous êtes sur la page 1sur 50

Chapitre 6

HÉRITAGE
Master SEIB
2020-2021
Azzedine DLIOU
Ecole Nationale des Sciences appliquées
Université Ibn Zohr
Plan
 Introduction
 Notion d’héritage
 Utilisation des membres de la classe de base
dans une classe dérivée
 Redéfinition des membres d’une classe
dérivée
 Appel des constructeurs et des destructeurs
 Contrôle des accès
 Compatibilité entre classes base et dérivée
Introduction
 Le concept d’héritage constitue l’un des
fondements de la P.O.O., il est à la base des
possibilités de réutilisation de composants
logiciels (en l’occurrence, de classes).
 Il vous autorise à définir 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 en ajoutant de
nouvelles, et cela sans qu’il soit nécessaire de
remettre en question la classe de base.
Introduction
 Cette technique permet de développer de
nouveaux outils en se fondant sur un certain
acquis, ce qui justifie le terme d’héritage.
 Plusieurs classes pourront être dérivées d’une
même classe de base.
 L’héritage n’est pas limité à un seul niveau : une
classe dérivée peut devenir à son tour classe de
base pour une autre classe.
 La notion d’héritage se voit comme un outil de
spécialisation croissante.
 C++ autorise, aussi, l’héritage multiple, grâce
auquel une classe peut être dérivée de plusieurs
classes de base.
Notion d’héritage
Soit la déclaration de la classe suivante :
class point {
int x ;
int y ;
public :
void initialise (int, int) ;
void deplace (int, int) ;
void affiche () const ;
};
 Supposons que nous ayons besoin de définir un nouveau type
classe nommé pointcol, destiné à manipuler des points
colorés d’un plan.
 Une telle classe peut disposer des mêmes fonctionnalités que
la classe point, auxquelles on pourrait adjoindre, par
exemple, une méthode nommée colore, chargée de définir la
couleur.
Notion d’héritage
 La déclaration de pointcol peut être la suivante :
class pointcol : public point //pointcol dérive de point
{
short couleur ;
public :
void colore (short cl) { couleur = cl ; }
};
 La déclaration :
class pointcol : public point
spécifie que pointcol est une classe dérivée de la classe de base
point.
 Le mot public signifie que les membres publics de la classe de
base (point) seront des membres publics de la classe dérivée
(pointcol).
 Il correspond à l’idée la plus fréquente que l’on peut avoir de
l’héritage, sur le plan général de la P.O.O.
Notion d’héritage
 La classe pointcol peut 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 (dans notre


exemple : colore) ;

◦ aux méthodes publiques de la classe de base


point (dans notre exemple : init, deplace et
affiche).
Notion d’héritage
Application
#include <iostream> class pointcol : public point
using namespace std ; {
class point { short couleur ;
int x ; public :
int y ; void colore (short cl)
public : { couleur = cl ; }
void initialise (int abs, int ord) };
{ x = abs ; y = ord ;} int main()
void deplace (int dx, int dy) {
{ x += dx ; y += dy ;} pointcol p ;
p.initialise (10,20) ;
void affiche () const { p.colore (5) ;
cout << "Je suis en " << x << p.affiche () ;
" " << y << "\n" ; p.deplace (2,4) ;
} p.affiche () ;
}; }
Utilisation des membres de la classe de
base dans une classe dérivée

 La classe pointcol 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 première façon d’améliorer cette situation


consiste à écrire une nouvelle fonction membre
publique, affichec, de pointcol, censée afficher à
la fois les coordonnées et la couleur.
Utilisation des membres de la classe de
base dans une classe dérivée
 On pourra penser définir affichec de la manière
suivante :
void affichec (){
cout << "Je suis en " << x << " " << y << "\n" ;
cout<<" ma couleur est : " << couleur << "\n";
}
cela signifierait que la fonction affichec, membre de
pointcol, aurait accès aux membres privés de
point, ce qui serait contraire au principe
d’encapsulation.
Utilisation des membres de la classe de
base dans une classe dérivée
 D’où la règle suivante adoptée par C++:
Règle
 Une méthode d’une classe dérivée n’a pas accès
aux membres privés de sa classe de base.

 En revanche, une méthode d’une classe dérivée a accès aux


membres publics de sa classe de base.
 Donc la fonction membre affichec peut faire appel à la fonction
affiche de la classe point.
 D’où la définition possible de affichec :
void pointcol::affichec () const {
affiche () ;
cout<<" et ma couleur est : "<<couleur<<"\n";
}
Utilisation des membres de la classe
de base dans une classe dérivée
Remarques
1. Au sein de affichec, nous avons fait directement
appel à affiche sans avoir à spécifier à quel objet
cette fonction devait être appliquée : par
convention, il s’agit de celui ayant appelé affichec.
2. Nous retrouvons la même règle que pour les
fonctions membres d’une même classe. En fait, il faut
désormais considérer que affiche est une fonction
membre de pointcol.
Utilisation des membres de la classe de
base dans une classe dérivée : Application
#include <iostream>
using namespace std ;
class point {
int x ;
int y ;
public :
void initialise (int abs, int ord)
{ x = abs ; y = ord ;}
void deplace (int dx, int dy)
{ x += dx ; y += dy ;}
void affiche () const
{ cout << "Je suis en " << x << " " << y << "\n" ;}
};
Utilisation des membres de la classe de
base dans une classe dérivée : Application
class pointcol : public point{
short couleur ;
public :
void colore (short cl) { couleur = cl ; }
void affichec () const ;
void initialisec (int, int, short) ;
};
void pointcol::affichec () const{
affiche () ;
cout << " et ma couleur est : " << couleur << "\n" ;
}
void pointcol::initialisec (int abs, int ord, short cl){
initialise (abs, ord) ;
couleur = cl ;
}
Utilisation des membres de la classe de
base dans une classe dérivée : Application
int 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
Cas des fonctions membres
 On pourrait souhaiter que les fonction affiche et affichec
puisse avoir le même nom.
 Ceci est effectivement possible en C++, moyennant une petite
précaution.
 Au sein de la fonction affiche de pointcol, on ne peut plus
appeler la fonction affiche de point comme auparavant, car cela
provoquerait un appel récursif de la fonction affiche de
pointcol.
 La solution est de faire appel à l’opérateur de résolution de
portée (::) pour localiser convenablement la méthode voulue
(ici, on appellera point::affiche).
 Si, pour un objet p de type pointcol, on appelle la fonction
p.affiche, il s’agira de la fonction redéfinie dans pointcol.
 Si l’on tient absolument à utiliser la fonction affiche de la classe
point, on appellera p.point::affiche.
Redéfinition des membres d’une classe
dérivée
Cas des fonctions membres : Application
#include <iostream>
using namespace std ;
class point {
int x ;
int y ;
public :
void initialise (int abs, int ord)
{ x = abs ; y = ord ;}
void deplace (int dx, int dy)
{ x += dx ; y += dy ;}
void affiche () const {
cout << "Je suis en " << x << " " << y << "\n" ;
}
};
Redéfinition des membres d’une classe
dérivée
Cas des fonctions membres : Application
class pointcol : public point {
short couleur ;
public :
void colore (short cl) { couleur = cl ; }
void affiche () const ; // redéfinition de affiche de point
void initialise (int, int, short) ; // redéfinition de initialise de point
};
void pointcol::affiche () const {
point::affiche () ; // appel de affiche de la classe point
cout << " et ma couleur est : " << couleur << "\n" ;
}
void pointcol::initialise (int abs, int ord, short cl) {
point::initialise (abs, ord) ; // appel de initialise de point
couleur = cl ;
}
Redéfinition des membres d’une classe
dérivée
Cas des fonctions membres : Application
int 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
Cas des fonctions membres : Application-Résultat

Je suis en 10 20
et ma couleur est : 5
Je suis en 10 20
Je suis en 12 24
et ma couleur est : 5
Je suis en 12 24
et ma couleur est : 2
Redéfinition des membres d’une classe
dérivée
Redéfinition et surdéfinition
• Soit les instructions suivantes :
class A { class B : public A {
public : public :
void f(int n) { ..... } void f(float x) { ..... }
void f(char c) { ..... } // on ajoute 3ème définition dans B
// f est surdéfinie dans A };
};
int main(){
int n ; char c ; A a ; B b ;
a.f(n) ; a.f(c) ; // appelle A:f(int) et 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)
}
Redéfinition des membres d’une classe
dérivée
Redéfinition et surdéfinition : Explication

 On a ajouté dans la classe B une troisième version


de f pour le type float.
 Pour résoudre les appels b.f(n) et b.f(c), le
compilateur n’a considéré que la fonction f de B qui
s’est trouvée appelée dans les deux cas.
 Si aucune fonction f n’avait été définie dans B, on
aurait utilisé les fonctions f(int) et f(char) de A.
Redéfinition des membres d’une classe
dérivée
Redéfinition et surdéfinition
• Le même phénomène se produirait si l’on effectuait dans B une
redéfinition de l’une des fonctions f de A, comme par exemple :
class A { class B : public A {
public : public :
void f(int n) { ..... } void f(int n) { ..... }
void f(char c) { ..... } // on redefinit f(int) dans B
// f est surdéfinie dans A };
};
int main(){
int n ; char c ; B b ;
b.f(n) ; // appelle B:f(int)
b.f(c) ; // appelle B:f(int)
}
• On remarque qu’une redéfinition d’une méthode dans une
classe dérivée cache en quelque sorte les autres.
Redéfinition des membres d’une classe
dérivée
Redéfinition et surdéfinition
• Voici un dernier exemple :
class A { class B : public A {
public : public :
void f(int n) { ..... } void f(int, int) { ..... }
void f(char c) { ..... } // on redefinit f(int) dans B
// f est surdéfinie dans A };
};
int main(){
int n ; char c ; B b ;
b.f(n) ; // Erreur de compilation
b.f(c) ; // Erreur de compilation
}
• Pour les appels b.f(n) et b.f(c), le compilateur n’a considéré
que l’unique fonction f(int, int) de B, laquelle ne convient
manifestement pas.
Redéfinition des membres d’une classe
dérivée
Redéfinition et surdéfinition :
Remarques
1. 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).
2. 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 (ou d’une classe ascendante), mais jamais dans plusieurs
classes à la fois.
3. Cette particularité peut être employée pour interdire l’emploi
dans une classe dérivée d’une fonction membre d’une classe de base :
il suffit d’y définir une fonction privée de même nom (peu importent
ses arguments et sa valeur de retour).
4. Il est possible d’imposer que la recherche d’une fonction surdéfinie
se fasse dans plusieurs classes en utilisant une directive using. Par
exemple, si dans la classe B précédente, on introduit (à un niveau
public) l’instruction :
using A::f ; // on réintroduit les fonctions f de A
Appel des constructeurs et des
destructeurs
hiérarchisation des appels
 Les règles (déjà vus) des appels du constructeur et du
destructeur se généralisent au cas des classes
dérivées.
 Soit les classes suivantes :
class A { class B : public A {
public : public :
A() { ..... } B() { ..... }
~A(){ ..... } ~B(){ ..... }
…. ….
}; };
Appel des constructeurs et des
destructeurs
hiérarchisation des appels
 Pour créer un objet de type B, il faut tout d’abord créer un
objet de type A, donc faire appel au constructeur de A, puis le
compléter par ce qui est spécifique à B et faire appel au
constructeur de B.

 Ce mécanisme est pris en charge par C++ : il n’y aura pas à


prévoir dans le constructeur de B l’appel du constructeur de A.

 La même démarche s’applique aux destructeurs : lors de la


destruction d’un objet de type B, il y aura automatiquement appel
du destructeur de B, puis appel de celui de A (les destructeurs
sont appelés dans l’ordre inverse de l’appel des constructeurs).
Appel des constructeurs et des
destructeurs
Transmission d’informations entre constructeurs
 En fait, C++ a prévu la possibilité de spécifier, dans la
définition d’un constructeur d’une classe dérivée, les
informations que l’on souhaite transmettre à un
constructeur de la classe de base.
 Le mécanisme est le même que celui que nous vous avons
exposé dans le cas des objets membres.
 Par exemple ceci :
class point { class pointcol {
…. ….
public : public :
point(int,int){ ..... } pointcol(int,int,char) …{ ..... }
…. ….
}; };
Appel des constructeurs et des
destructeurs
Transmission d’informations entre constructeurs
 Si on souhaite que pointcol retransmette à point les deux
premières informations reçues, on écrira son en-tête de
cette manière :
pointcol (int abs, int ord, char cl) : point (abs, ord)
 Le compilateur mettra en place la transmission au
constructeur de point des informations abs et ord
correspondant (ici) aux deux premiers arguments de
pointcol. Ainsi, la déclaration :
pointcol a (10, 15, 3);
entraînera :
1. l’appel de point qui recevra les arguments 10 et 15 ;
2. l’appel de pointcol qui recevra les arguments 10, 15 et 3.
Appel des constructeurs et des
destructeurs
Transmission d’informations entre constructeurs
#include <iostream>
using namespace std ;
// ************ classe point *********************
class point {
int x, y ;
public :
point (int abs=0, int ord=0) {
cout<<"++ constr. point : "<< abs <<" "<< ord << "\n";
x = abs ; y =ord ;
}
~point (){
cout << "-- destr. point : " << x << " " << y << "\n" ;
}
};
Appel des constructeurs et des
destructeurs
Transmission d’informations entre constructeurs
// ************ classe pointcol ******************
class pointcol : public point
{
short couleur ;
public :
pointcol (int, int, short) ;
~pointcol (){
cout<<"-- dest. pointcol - couleur : " <<couleur<<"\n";
}
};
pointcol::pointcol(int abs=0, int ord=0, short cl=1) : point (abs, ord)
{
cout<<"++ constr. pointcol : "<<abs<<" "<<ord<<" "<<cl<<"\n";
couleur = cl ;
}
Appel des constructeurs et des
destructeurs
Transmission d’informations entre constructeurs

// ************ programme d’essai ****************


int main()
{
pointcol a(10,15,3) ;
pointcol b (2,3) ;
pointcol c (12) ;
pointcol * adr ;
adr = new pointcol (12,25) ;
delete adr ;
}
Appel des constructeurs et des
destructeurs
Transmission d’informations entre constructeurs

++ constr. point : 10 15 -- dest. pointcol - couleur : 1


++ constr. pointcol : 10 15 3 -- destr. point : 12 25
++ constr. point : 2 3 -- dest. pointcol - couleur : 1
++ constr. pointcol : 2 3 1 -- destr. point : 12 0
++ constr. point : 12 0 -- dest. pointcol - couleur : 1
++ constr. pointcol : 12 0 1 -- destr. point : 2 3
++ constr. point : 12 25 -- dest. pointcol - couleur : 3
++ constr. pointcol : 12 25 1 -- destr. point : 10 15
Contrôle des accès
Présentation
 La situation d’héritage examinée jusqu’ici est celle dans laquelle :
◦ La classe dérivée (c-à-d ces fonctions membres) a accès aux
membres publics de la classe de base ;
◦ Les «utilisateurs» (c-à-d objet du type de la classe dérivée) de
la classe dérivée ont accès à ses membres publics, ainsi qu’aux
membres publics de sa classe de base.
 C++ permet d’intervenir en partie sur ces deux sortes
d’autorisation d’accès, et ce à deux niveaux :
◦ Lors de la conception de la classe de base : en plus des
statuts publics et privés, il existe un troisième statut dit
«protégé» (mot-clé protected).
Les membre protégés se comportent comme des membres
privés pour l’utilisateur de la classe dérivée mais comme des
membres publics pour la classe dérivée elle-même.
◦ Lors de la conception de la classe dérivée : on peut
restreindre les possibilités d’accès aux membres de la classe de
base.
Contrôle des accès
Membres protégés
 Il existe donc trois « statuts » possibles pour un membre
de classe :
1. privé : le membre n’est accessible qu’aux fonctions
membres (publiques ou privées) et aux fonctions
amies de la classe ;
2. public : le membre est accessible non seulement aux
fonctions membres ou aux fonctions amies, mais
également à l’utilisateur de la classe (c’est-à-dire à
n’importe quel objet du type de cette classe).
3. Protégé : ils seront accessibles aux membres d’une
éventuelle classe dérivée, tout en restant dans tous
les cas inaccessibles aux utilisateurs de cette classe,
pour qui ils apparaissent comme des membres privés.
Contrôle des accès
Exemple
 Soit la classe point : • La définition suivante de affiche,
dans pointcol, devient possible :
class point { class pointcol : public point {
protected : short couleur ;
int x, y ; public :
public : void affiche () const{
point ( ... ) ; cout << "Je suis en "
affiche () ; << x<<" "<<y<< "\n";
..... cout << " et ma couleur
}; est "<<couleur<<"\n";
}
}
Contrôle des accès
Remarques

Remarques
1. Lorsqu’une classe dérivée possède des fonctions
amies, ces dernières disposent exactement des
mêmes autorisations d’accès que les fonctions
membres de la classe dérivée.
2. Les déclarations d’amitié ne s’héritent pas. Ainsi,
si f a été déclarée amie d’une classe A et si B
dérive de A, f n’est pas automatiquement amie de
B (il est bien sûr possible de prévoir une
déclaration d’amitié appropriée dans B).
Contrôle des accès
Dérivation publique : Rappels
Contrôle des accès
Dérivation privée
• En utilisant le mot-clé private au lieu du mot-clé public, il est possible
d’interdire à un utilisateur d’une classe dérivée l’accès aux membres
publics de sa classe de base.
• Par exemple :
class point class pointcol : private point
{ {
..... .....
public : public :
point (...) ; pointcol (...) ;
void affiche () ; void colore (...) ;
void deplace (...) ; ...
... };
};
• Si p est de type pointcol, les appels suivants seront rejetés par le
compilateur :
p.affiche (); /* ou même : p.point::affiche () */
p.deplace (...); /* ou même : p.point::deplace (...) */
alors que, naturellement, celui-ci sera accepté : p.colore (...);
Contrôle des accès
Dérivation privée
• Cette technique de fermeture des accès à la classe de
base ne sera employée que dans des cas bien précis, par
exemple :
o Lorsque 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 ;
o Lorsque l’on souhaite adapter l’interface d’une classe
de manière à répondre à certaines exigences.
Contrôle des accès
Dérivation protégée
• C++ dispose d’une possibilité supplémentaire de dérivation,
dite dérivation protégée, intermédiaire entre la dérivation
publique et la dérivation privée.
• Dans ce cas, les membres publics de la classe de base seront
considérés comme protégés lors de dérivation ultérieures.
Remarques
1. Ne pas confondre le mode de dérivation d’une classe par rapport
à sa classe de base (publique, protégée ou privée), définie par l’un
des mots public, protected ou private, avec le statut des
membres d’une classe (public, protégé ou privé) défini également
par l’un de ces trois mots.
2. Dans le cas d’une dérivation privée, les membres protégés de la
classe de base restent accessibles aux fonctions membres et aux
fonctions amies de la classe dérivée. En revanche, ils seront
considérés comme privés pour une dérivation future.
Contrôle des accès
Récapitulation
• Voici un tableau récapitulant les propriétés des différentes sortes de
dérivation :
o La mention « Accès FMA » signifie : accès aux fonctions membres
ou amies de la classe ;
o La mention « nouveau statut » signifie : statut qu’aura ce
membre dans une éventuelle classe dérivée.

Remarque
 Une dérivation protégée ne se distingue d’une dérivation privée que
lorsque l’on est amené à dériver de nouvelles classes de la classe
dérivée en question.
Compatibilité entre classes de base et
dérivée
Conversion d’un type dérivé en un type de base
• Soit les deux classes :
class point class pointcol : public point
{ ..... } { ..... }
• Avec les déclarations :
point a ;
pointcol b ;
l’affectation :
a=b;
est légale. Elle entraîne une conversion de b dans le type point
(cette conversion revient à ne conserver de b que ce qui est du type
point) et l’affectation du résultat à a.
• Cette affectation se fait, suivant les cas :
o Par appel de l’opérateur d’affectation (de la classe point) si
celui-ci a été surdéfini ;
o Par emploi de l’affectation par défaut dans le cas contraire.
• En revanche, l’affectation suivante serait rejetée :
b=a;
Compatibilité entre classes de base et
dérivée
Conversion de pointeurs
• Soit les deux classes :
class point class pointcol : public point
{ {
int x, y ; short couleur ;
public : public :
..... .....
void affiche () ; void affiche () ;
..... .....
}; };
• Soit ces déclarations :
point * adp ; pointcol * adpc ;
• Là encore, C++ autorise l’affectation : adp = adpc ;
qui correspond à une conversion du type pointcol * dans le type
point *.
• L’affectation inverse : adpc = adp ;
serait naturellement rejetée.
Compatibilité entre classes de base et
dérivée
Limitations liées au typage statique des objets
• Considérons les déclarations suivantes des classes précédentes :
point p (3, 5) ; pointcol pc (8, 6, 2) ;
adp = & p ; adpc = & pc ;
• La situation est alors celle-ci :

• L’instruction : adp -> affiche () ;


appellera la méthode point::affiche,
tandis que l’instruction : adpc -> affiche () ;
appellera la méthode pointcol::affiche.
• Les mêmes résultats sont obtenus avec :
p.affiche () ; pc.affiche () ;
Compatibilité entre classes de base et
dérivée
Limitations liées au typage statique des objets
• Si nous exécutons l’affectation :
adp = adpc ;
nous aboutissons à cette situation :

• Une instruction telle que :


adp -> affiche () ;
fera appel à point::affiche ou à pointcol::affiche?
Compatibilité entre classes de base et
dérivée
Limitations liées au typage statique des objets
• adp est du type point mais l’objet pointé par adp est du type
pointcol.
• Le choix de la méthode appelée est réalisé par le compilateur.
• La fonction appelée est donc point::affiche, puisque adp est
du type point *.
• Le compilateur ne peut que décider de mettre en place
l’appel de la méthode correspondant au type défini par le
pointeur.
• Si pointcol dispose d’une méthode colore (n’existant pas dans
point), un appel tel que :
adp -> colore (8) ;
sera rejeté par le compilateur.
• Le type des objets pointés par adp et adpc est décidé et figé
au moment de la compilation.
Compatibilité entre classes de base et
dérivée
Exemple
#include <iostream> class pointcol : public point
using namespace std ; {
class point short couleur ;
{ public :
protected : pointcol (int abs=0, int ord=0, short
int x, y ; cl=1) : point (abs, ord)
public : { couleur = cl ;}
point (int abs=0, int ord=0) void affiche () const
{ x=abs ; y=ord ; } {
void affiche () const cout<<"Je suis un point colore \n" ;
{ cout << " mes coordonnees sont :
cout << "Je suis un point \n"; " << x << " " << y ;
cout << " mes coordonnees cout << " et ma couleur est : " <<
sont : " << x << " " << y couleur << "\n" ;
<< "\n" ; }
} };
};
Compatibilité entre classes de base et
dérivée
Exemple
int main()
{
point p(3,5) ;
point * adp = &p ;
pointcol pc (8,6,2) ;
pointcol * adpc = &pc ;
adp->affiche () ;
adpc->affiche () ;
cout << "-----------------\n" ;
adp = adpc ;
adp->affiche () ;
adpc->affiche () ;
}
Compatibilité entre classes de base et
dérivée
Résultat

Je suis un point
mes coordonnees sont : 3 5
Je suis un point colore
mes coordonnees sont : 8 6 et ma couleur est : 2
-----------------
Je suis un point
mes coordonnees sont : 8 6
Je suis un point colore
mes coordonnees sont : 8 6 et ma couleur est : 2

Vous aimerez peut-être aussi