Title: 5i
15ième Classe (Mercredi, 3 Novembre)CSI2572
2Héritage
- Il est possible d'utiliser un objet d'une classe
dérivée partout où l'on peut utiliser un objet
d'une de ses classes mères. Les méthodes et
données des classes mères appartiennent en effet
par héritage aux classes filles. Bien entendu, on
doit avoir les droits d'accès sur les membres de
la classe de base que l'on utilise (l'accès peut
être restreint lors de l'héritage). - il est possible de faire une affectation d'une
classe dérivée vers une classe mère. Les données
qui ne servent pas à l'initialisation sont
perdues, puisque la classe mère ne possède pas
les champs correspondants. En revanche, l'inverse
est strictement interdit. En effet, les données
de la classe fille qui n'existent pas dans la
classe mère ne pourraient pas recevoir de valeur,
et l'initialisation ne se ferait pas
correctement.
3Héritage
- Les pointeurs des classes dérivées sont
compatibles avec les pointeurs des classes mères.
Cela signifie qu'il est possible d'affecter un
pointeur de classe dérivée à un pointeur d'une de
ses classes de base. Il faut bien entendu que
l'on ait en outre le droit d'accéder à la classe
de base, c'est-à-dire qu'au moins un de ses
membres puisse être utilisé. Cette condition
n'est pas toujours vérifiée, en particulier pour
les classes de base dont l'héritage est private. - Un objet dérivé pointé par un pointeur d'une des
classes mères de sa classe est considéré comme un
objet de la classe du pointeur qui le pointe. Les
données spécifiques à sa classe ne sont pas
supprimées, elles sont seulement momentanément
inaccessibles. C'est ce à quoi sert le mécanisme
des méthodes virtuelles
4 class Mere public Mere(void)
Mere(void) class Fille public Mere
public Fille(void) Fille(void) Mere
m // Instanciation de deux objets. Fille f
mf //OK fm // ERREUR !! (ne compile pas).
5Avec pointeurs
Mere pm, m Fille pf, f pf f //
Autorisé. pm pf // Autorisé. Les données et
les méthodes // de la classe fille ne sont plus
accessibles // avec ce pointeur pm est un
objet // de la classe mère. pfm // ILLÉGAL
il faut faire un cast pf(Fille ) m //
Cette fois, c'est légal, mais DANGEREUX ! // En
effet, les méthodes de la classe filles // ne
sont pas définies, puisque m est une classe //
mère.
6Héritage virtual
- L'utilisation d'un pointeur sur la classe de
base pour accéder à une classe dérivée nécessite
d'utiliser des méthodes virtuelles. En
particulier, il est nécessaire de rendre virtuels
les destructeurs. Par exemple, avec la définition
donnée ci-dessus pour les deux classes, le code
suivant est faux - Mere pm
- Fille pf new Fille
- pm pf
- delete pm // Appel du destructeur de la classe
mère !
7Héritage virtual
- Pour résoudre le problème, il faut que le
destructeur de la classe mère soit virtuel (il
est inutile de déclarer virtuel le destructeur
des classes filles) - class Mere
- public Mere(void)
- virtual Mere(void)
-
- Le bon destructeur est appelé, car le destructeur
est déclaré virtual. En effet, l'opérateur delete
recherche le destructeur à appeler dans la classe
de l'objet le plus dérivé. De plus, l'opérateur
delete restitue la mémoire de l'objet complet, et
pas seulement celle du sous-objet référencé par
le pointeur utilisé dans l'expression delete.
8Héritage virtual
- Lorsqu'on utilise la dérivation, il est donc
très important de déclarer les destructeurs
virtuels pour que l'opérateur delete utilise le
vrai type de l'objet à détruire. - Bien sur, il est aussi nécéssaire d'utiliser le
méchanisme virtuel dans toutes les situations ou
une instance sera référée par un pointeur typé
sur la classe mère de sa classe.
9 include ltiostream.hgt class A public
void foo()coutltlt "Afoo()"ltltendl class
B public A public void foo()coutltlt
"Bfoo()"ltltendl int main(void) A a B
b A p b A r b p-gtfoo() r.foo() a
b a.foo() return 0
Afoo() Afoo() Afoo()
10 class shape private char name public
shape(const char) shape(const shape)
shape operator(const shape) virtual
shape() // pure virtual function virtual
double area() const 0 const char
get_name() const
11 shapeshape(const char n) name(new
charstrlen(n)1) strcpy(name,n)
shape shapeoperator(const shape s)
delete name name new
charstrlen(s.name)1 strcpy(name,s.name)
return this shapeshape(const shape
s) name(new charstrlen(s.get_name())1)
strcpy(name,s.name) shapeshape()
delete name const char
shapeget_name() const return name
12 class rectangle public shape private
double a,b public rectangle(double,doubl
e) rectangle(const rectangle) rectangle
operator(const rectangle) virtual
rectangle() virtual double area() const
13rectanglerectangle(double i, double
j) shape("rectangle"),a(i),b(j) rectangle
rectangleoperator(const rectangle r)
shapeoperator(r) a r.a b r.b
return this rectanglerectangle(const
rectangle r) shape(r) a r.a b r.b
rectanglerectangle() double
rectanglearea() const return ab
14class circle public shape private double
r public circle(double) circle(const
circle) circle operator(const circle)
circle() double area() const
15circlecircle(double i) shape("circle"),r(i)
circle circleoperator(const circle c)
shapeoperator(c) r c.r return this
circlecircle(const circle c)shape(c)
r c.r circlecircle() double
circlearea() const return 3.14159rr
16 const int L5 int main() shape shapes
new shapeL int i0 while (i lt L)
if() shapesi new circle(r)
else shapesi new
rectangle(a,b) i for(int i0 iltL,
i) cout ltlt shapesi-gtget_name() ltlt " with
area " ltlt shapesi-gtarea() ltlt endl delete
shapesi
17Classes Abstraites
- Une classe qui ne possède pas dinstance est
dite abstraite. Cest forcément le cas si elle ne
fournit pas d'implémentation pour certaines de
ces méthodes. Ces méthodes seront alors appelées
méthodes abstraites. - Ce sera les classes dérivées des classes
abstraites qui auront une définition du code des
méthodes abstraites. On pourra alors parler de
classes concrètes les classes concrètes étant
les seules à même dêtre instanciées. - Les classes abstraites définissent un cadre de
travail pour les classes dérivées en proposant un
ensemble de méthodes que l'on retrouvera tout au
long de l'arborescence. Ce mécanisme est
fondamental pour la mise en place du
polymorphism.
18Classes Abstraites
- Pour certaines classes, il est tout à fait
naturel de ne pas avoir d'instances un véhicule
ne correspond à aucun objet concret mais plutôt
au concept d'un objet capable de démarrer,
ralentir, accélérer ou s'arrêter, que ce soit une
voiture, un camion ou un avion. - Une classe ne peux être abstraite que si au
moins une de ses méthodes virtuelles est pure.
Pour déclarer une méthode virtuelle pure, il
suffit de ne pas donner dimplantation et
décrire - 0
- derrière sa déclaration. Seules les fonctions
virtuelles peuvent être déclarées pures, sous
peine derreur.
19Classes Abstraites
class liste // classe abstraite protected
int nombre public virtual liste()
nombre 0 virtual void avance(int combien
1) 0 // pure void recule(int combien
1) avance(-combien) virtual
element valeur(void) 0 unsigned
nombre_elt(void) return nombre
void affiche(unsigned combien 65535)
virtual int insere(const element) 0
void supprime(int n 1) 0
- Lorsquune classe est abstraite, elle ne peut
être utilisée directement en particulier, on ne
peut pas déclarer dobjets de cette classe, ni
darguments ou de résultats de fonction. - On peut par contre utiliser des références, des
pointeurs et dériver de nouvelles classes, et
cest en fait lusage de ces classes abstraites.
20Classes Abstraites
class listech public liste // liste
chaînée noeud courant public listech()
nombre 0 courant 0
listech(int n, const element) listech()
void avance(int combien 1) element
valeur(void) if (courant) return
courant-gtcontenu() int insere(const
element) void supprime(int n 1)
21Classes Abstraites
class listetab public liste element tab,
courant public listetab() courant
tab 0 listetab(int n, const element)
listetab() void avance(int combien 1)
element valeur(void) if (courant)
return courant int insere(const
element) void supprime(int n 1)
22Classes Abstraites
- La classe liste est abstraite, puisque quatre de
ses méthodes ont été déclarées pures - Les classes abstraites nont généralement pas de
constructeur, sauf si linitialisation des
membres est un peu compliquée (ici il suffit de
mettre la valeur adéquate dans le champ nombre,
et les constructeurs de listch et listtab le
font). Par contre, il est généralement
souhaitable dy placer un destructeur virtuel,
même sil ne fait rien comme dans notre exemple
on est ainsi certain de la bonne destruction des
objets des classes dérivées.
23Héritage Multiple
- Il est parfaitement possible quune classe
hérite de plusieurs classes. Voici un exemple - La classe C hérite de manière publique de A et de
manière privée de B (il faut préciser à chaque
classe le type de dérivation, sinon cest le type
par défaut qui sapplique). Elle a trois sortes
de membres les siens propres ceux hérités de
A ceux hérités de B. Les règles dhéritage sont
les mêmes que dans lhéritage simple.
class A // ... class B // ... class
C public A, B // ...
24Héritage Multiple
- Le constructeur de C appelle les constructeurs
de A et B, implicitement ou non - CC() A(), B() // ...
-
- Noter que dans cette écriture, tout comme dans la
déclaration dhéritage, cest une virgule qui
sépare les différentes classes de base, et non le
symbole deux-points. -
25Héritage Multiple
-
- Lorsquune classe hérite de plusieurs autres, il
se peut que deux des classes de base aient des
champs ou des méthodes ayant le même nom. - Sil sagit dun champ dune part, et dune
méthode dautre part, ou de deux méthodes mais
avec des listes darguments différents, il ny a
pas dambiguïté et le compilateur se débrouillera
en fonction du contexte dutilisation. - Par contre, lorsquil sagit de deux champs, ou
de deux classes A et B ont toutes deux un champ
x, il faudra écrire - C c
- c.Ax 0
26Héritage Multiple
-
- Lorsquil sagit de méthodes, il est préférable
de recouvrir les méthodes de base en déclarant
une méthode dans la nouvelle classe ayant le même
nom et les mêmes arguments. - Pour les méthodes ayant les mêmes arguments, le
compilateur se trouve face à une ambiguïté
insoluble. Pour la résoudre, il faut utiliser le
nom dune des classes de base et lopérateur de
résolution de portée. -