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

memoireMixe

Construction, déstruction

Exemple

class Mixe: public A, public B {
    Mixe(...): A(...), B(...), attributs { ... };
};

En reprenant l’exemple précédent, B est détruit, puis A lors de l’appel du destructeur de Mixe.

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;
}