Fonction virtuelle et héritage multiple
Fonction virtuelle
Rappel
graph TD
B --> b1 & b2 & b3
Si une classe D dérive de B, il existe une conversion implicite de D\star à B\star ainsi que de D\\\& à B\\\&.
Exemple
D* p = new D;
B* pb = p;
Pour les appels de fonction ?
struct B {
void f(void) { ... } // f1
virtual void v(void) { ... } // v1
void h(void) {
...
v(); // équivalent à this->v(); ou (*this).v();
...
}
};
struct D: public B {
void f(void) { ... } // f2
void v(void) { ... } // v2
};
int main(void) {
B * p = new D;
p->f(); // appel f1 car p est de type B
p->v(); // appel v2 car v1 virtual
p->h(); // appel v2 dans h
return 0;
}
Pour f()
le choix de la fonction est définie à la compilation.
On peut forcer le compilateur a ne pas décider à la compilation et de laisser le choix à l’exécution avec le mot clé virtual
comme fait pour v()
.
-> Si une méthode est déclarée virtual
alors toutes ses définitions seront virtual
et le choix d’une méthode f à partir d’une adresse est fait :
- à l’exécution si f est déclarée virtual
- à la compilation selon le type de l’adresse si f n’est pas virtual
Remarques
Cette règle est aussi valable lorsqu’on utilise this
(dans une méthode) explicitement ou implicitement.
Fonction “virtuelle pure”
On dit qu’une fonction est virtuelle pure si elle est déclarée sans être définie
Exemple
class A {
virtual int maMethodeVirtuellePure(void) = 0;
// virtual int maMethodeVirtuellePure(void); // -> erreur
};
Une classe qui possède une méthode virtuelle pure ne peut pas “fabriquer d’objet” (ne peut pas être instanciée). On dit que c’est une classe abstraite.
Une classe dérivée d’une classe abstraite A peut-être instanciée seulement si elle définit chacune des méthodes virtuelles de A.
Héritage multiple
En C++ une classe peut dériver de plusieurs autre classes.
Exemple
class A { ... };
class B { ... };
class Mixe: public A, public B { ... };
La nouvelle classe Mixe
héritera des champs et des méthodes des deux classes A
et B
.
En mémoire
Construction, déstruction
- Si une classe
Mixe
dérive de des classesA1
,A2
,A3
, … dans cet ordre, les constructeurs des classes mèresA1
,A2
doient être appelé dans cet ordre et avant les constructeurs des nouveaux attributs définis dansMixe
.
Exemple
class Mixe: public A, public B {
Mixe(...): A(...), B(...), attributs { ... };
};
- Par défaut, le déstructeur appelle les déstructeurs des classes mère dans l’ordre inverse de l’héritage.
En reprenant l’exemple précédent,
B
est détruit, puisA
lors de l’appel du destructeur deMixe
.
Remarque
Pour lever l’ambiguïté de deux membres ayant les mêmes nom/signature, on utilise ::
Exemple
int main(void) {
Mixe m;
m.A::attrib(..., B::attrib);
return 0;
}