Héritages

On peut utiliser le comportement d’une classe existante pour construire une nouvelle classe “modifiée”.

Exemple

struct Base { // classe de base ; classe mère
    void fonctionBase() { ... }
};

struct Derivee: public Base { // classe dérivée de la base ; classe fille
    int a;

    void fonctionDerivee() { ... }
};

int main(void) {
    Base objB;
    Derivee objD;

    objD.fonctionDerivee(); // oui ça fonctionne
    objD.fonctionBase();    // oui ça fonctionne

    objB.fonctionBase();       // oui ça fonctionne
    // objB.fonctionDerivee(); /* provoque une erreur
                               /* pas de méthode dérivée
                                * dans la classe de base. */

    return 0;
}

En mémoire

Si B est la classe de base :
enMemoire_B
Si D hérite de B avec :

struct D: public B {
    int a1;
    int a2;
    int a3;
    // ...
    int ak;
}

enMemoire_D

Remarques

Exemple

\rightarrow : hérite de…

flowchart BT
Soldat --> 1[Personnage]
Architecte --> 1
class Personnage {
    int pos_x;
    int pos_y;
    int pointVie;

    public:
        // Permet de se déplacer vers une nouvelle position
        void deplacer(int x, int y);

        // Diminiue les points de vie du personnage
        void prendDegat(int ptDegats);

        // ...
};

class Soldat: public Personnage {
    int puissance; // puissance d'attaque

    public:
        // Permet d'attaquer un autre personnage
        void attaque(Personnage &p) { // polymorphisme
            a.prendDegats(puissance);
        }
};

class Architecte: public Personnage {
    int efficacite; // efficacite a construire des bâtiments

    public:
        // Construit un bâtiment
        void construire();
};

int main(void) {
    Soldat s1;
    Soldat s2;
    Architecte a1;
    // ...

    s1.attaque(s2);
    s1.attaque(a1);
    // ...

    return 0;
}

Redéfinition

struct A {
    void f(void) {
        std::cout << "je suis un objet A" << std::endl;
    }

    void f(int a) {
        std::cout << "je suis un objet A " << a << std::endl;
    }

};

struct B: public A {
    void f(void) {
        std::cout << "je suis un objet B" << std::endl;
    }
};

int main(void) {
    A a;
    B b;

    a.f(); // affiche: je suis un objet A
    b.f(); // affiche: je suis un objet B

    b.f(3); // affiche: je suis un objet A 3

    return 0;
}

La redéfinition “cache” l’ancienne méthode. Elle est pourtant toujours accessible.

Exemple : b.A::f(); -> “je suis un objet A”

Remarque

Membres protected d’une classe

Un membre d’une classe appartient à un et un seul des groupes suivants :

Un membre protected d’une classe M est inaccessible hors de la classe M sauf dans une méthode d’une classe dérivée de la classe M.

Exemple

class A {
    protected:
        int a;
    public:
        A(void) { ... }
};

class B: public A {
    /* ... */

    void f(void) {
        a = 3; // ok accès à un protected
    }

    /* ... */
};

int main(void) {
    A objA;
    B objB;

    // objA.a = 0; // erreur l'attribut a n'est pas accessible
    // objB.a = 1; // erreur l'attribut a n'est pas accessible

    return 0;
}

Autres types d’héritages en C++

Appels aux constructeurs/destructeurs de la classe de base

struct A {
    A(void) { ... } // constructeur A1
    A(int a) { ... } // constructeur A2
};

struct B: public A {
    int e;
    int d;

    /*        |-> tout ça c'est
     *        |   des initialisateurs
     *        \______________ */
    B(int i): A(i), e(3), d(7) { ... }
    /*        A(i) -> appel au
     *                constructeur
     *                A2 */
};

Conversions implicites

Si la classe F hérite de M, alors tout objet de type F sera considéré comme un objet de type M (cf. exemple avec les personnages, soldats et architecte).

Exemple

F* pf = new F(...);
M* pm = pf;
// pf = pm; // erreur

Si void m(void) est une méthode de M qui est redéfinie dans la classe F

pm->m(); /* si void m(void)
          * n'est pas "virtual"
          * appel a void M::m(void) */

Pour éviter ce comportement il faut déclarer mvirtual”.

class M {
    virtual void m(void) { ... }
};

class F: public M {
    void m(void) { ... }
};

Ici le choix de la méthode m (qui est virtual) a partir d’un pointeur sera effectué à l’exécution et pas à la compilation.