|

Écrivez
à AILES ! |

Retour
vers programmation |

Retour
vers les questions C++ |
|
|
Questionnaire C++
Héritage multiple
[AnnotatedC++],
pp.198 et suiv.
|
L'héritage multiple provoque, de nos
jours (1999), une réaction de plus en plus
timide : on trouve cela "dangereux",
"risqué", voir même "preuve
d'une mauvaise modélisation"!...
Java a depuis longtemps tranché et en a purement
et simplement interdit l'usage, lui préférant
"l'implémentation d'interfaces
multiples".
Pourquoi donc ? Le petit questionnaire suivant va
vous le démontrer : gérer en mémoire des
instances issues d'un héritage multiple, cela
n'a rien d'évident. |
A
quoi sert l'héritage multiple ?
(Réponses à toutes les questions de cette
page)
Classe de base :
une classe dérivée peut-elle hériter deux fois d'une
même classe de base :
- directement ?
class
D1 : public B, public B { /* ... */ } ;
- indirectement ?
class
A : public L { /* ... */ } ;
class B : public L { /* ... */ } ;
class D2 : public A, public B { /* ... */ } ;
- dans le
deuxième cas, quel est le graphe acyclique dirigé
décrivant le mieux la situation ?
 |
ou bien |

|
Peut-on caster D2
en L ?
(Réponses à toutes les questions de cette
page)
Héritage virtuel
Décrivez par un graphe acyclique
dirigé la situation suivante :
class
B : { /* ... */ } ;
class X : virtual public B { /* ... */ } ;
class Y : virtual public B { /* ... */ } ;
class Z : public B { /* ... */ } ;
class DDD : public X, public Y, public Z { /* ... */ } ;
Dans ce dernier cas, comment la fonction virtuelle f() de
B, redéfinie dans X, Y, Z doit elle être implémentée
dans DDD afin d'appeler sans conflit chacune des versions
de f() de ses classes mères ?
class B
{
public:
virtual void f() ;
};
void
DDD::f()
{
...
X::f() ; Y::f() ; Z::f() ; // ? suffisant ?
}
(Réponses à toutes les questions de cette
page)
Ambiguïtés
C'est bon ? Pas trop mal à la tête
?... Allez, on passe la seconde.
class V
{
public:
int f() ;
int fff() ;
int v ;
int vvv ;
};
class A
{
public:
int a ;
static int static_a ;
enum {e} ;
};
class B : public A, public virtual V
{
public:
int f() ;
int x ;
int fff() ;
};
class C : public A, public virtual V
{
private:
char * vvv ;
};
class D : public B, public C
{
void g() ;
};
class DDD : public B, public virtual V
{
};
Avant de préciser si la fonction suivante comporte ou
non une ou plusieurs ambiguités, faite un diagramme
acyclique de la situation, avec les attributs et les
fonctions respectives de chaques classes.
Surtout, faites-le.
Surtout.
void
D:: g()
{
v++; //
ambigüe ou non ?
a++; //
ambigüe ou non ?
x++; //
ambigüe ou non ?
f() ; //
ambigüe ou non ?
static_a = 1 ; //
ambigüe ou non ?
int i = e ; //
ambigüe ou non ?
B* p_b = this ; //
ambigüe ou non ?
A* p_a = this ; //
ambigüe ou non ?
V* p_v = this ; //
ambigüe ou non ?
int my_vvv = vvv ;
}
Quelle
est la fonction f() appelée dans ggg() ? V::f() ou
B::f() ?
void ggg( B* p_b, DDD* p_ddd )
{
p_b->f() ; // V::f()
ou B::f() ?
p_ddd->f() ; // V::f()
ou B::f() ?
}
(Réponses à toutes les questions de cette
page)
Fonctions virtuelles
okok, on respire... on inspire... on
expire... on inspire un grand coup : c'est reparti.
Les questions sont en commentaires :
class
V
{
public :
static virtual int sf() ; // possible ?
virtual int f() ;
virtual int g( V* ) ;
virtual V* h() ;
};
class A : public V
{
int f() // f est-elle virtuelle
{
f() ; // quelle f() est appelé
?
};
virtual int g( A* ) ; // s'agit-il de la
même fonction virtuelle que V::g() ?
virtual A* h() ; // s'agit-il de la même
fonction virtuelle que V::h() ?
};
Considérez les classes A, B et C :
class A
{
public :
virtual void f() {
printf("A::f()") ; }
A() {}
};
class B : public A
{
public :
B() { f() ; } //
appelle la fonction virtuelle f()
void g() { f() ; } //
appelle la fonction virtuelle f()
};
class C : public B
{
public :
virtual void f() {
printf("C::f()") ; }
C() {}
};
Que produit le
code suivant ?
main()
{
C c ;
c.g() ;
}
Ultime question : fonctions virtuelles avec
héritage virtuel. soit :

Toutes les fonctions sont virtuelles. AL et BL ont un
héritage virtuel vers LL.
quelle est la fonction f() invoquée par le code suivant
?
void ma_fonction(
ABL* p_abl )
{
BL* p_bl = (BL*)p_abl ;
p_bl->f() ; //
LL::f() ou AL::f() ou error ???
}
Remarque : la dernière question de la prochaine
rubrique propose une autre façon de répondre à la
même question...
(Réponses à toutes les questions de cette
page)
Héritage multiple et représentation
en mémoire
Dernière ligne droite... et là,
ça devient vraiment coton...
Quelle est la représentation mémoire correcte d'un
objet de type C, sachant que C dérive des classes A et B
?
 |
ou bien |
 |
Considérez que A
possède une fonction fa(), B une fonction fb() et C un
fonction fc().
Comment, dans le cadre de la première représentation
mémoire (celle de gauche), traduiriez-vous :
C* p_c = new C() ;
p_c->fb() ; ?
Indice :
Les adresses mémoire de p_c et (B*)p_c sont-elles les
mêmes ?
Oui ?
Non ?
Si non, et si p_b = (B*)p_c, est-ce que (p_c == p_b) est
true ou false ?
Si non, pourquoi le code suivant marche-t-il quand-même
?
C*
p_c = 0 ;
B* p_b = 0 ;
if( p_c == 0 ) { /* ... */ }
p_b = p_c ;
if( p_b == 0 ) { /* ... */ }
En effet, si le
fait de caster un C* en B* devait entraîner un décalage
mémoire, p_b devrait changer puisque p_b vaut un cast
implicite de p_c en B*... alors ?
Quelle est la représentation mémoire d'un
héritage virtuel tel que celui-ci ?

Ultime question : héritage virtuel avec fonctions
virtuelles, le tout en mémoire!
Reprenons l'héritage virtuel présenté en fin de
rubrique précédente :

Représentez les tableaux de pointeurs vers les fonctions
virtuelles telles que chaque parties de l'objet ABL doit
les stocker.
Ces tableaux sont, pour chacune des fonctions f(), g(),
h() et k(), composé d'un offset permettant de retomber
sur le début de la bonne fonction.
Remarque : la dernière question
de la précédente rubrique propose une autre façon de
trouver un début de réponse à la même question...
(Réponses à toutes les questions de cette
page)
|