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 :
Si D hérite de B avec :
struct D: public B {
int a1;
int a2;
int a3;
// ...
int ak;
}
Remarques
- Les membres privés de la classe mère sont inaccessibles dans la classe fille. Par contre les membres publiques restent publiques.
- Pas d’héritage des constructeurs (constructeurs de recopie), destructeur, opérateur d’affection. Par défaut ils sont générés à partir des méthodes de la classe de base.
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
- de méthode
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
- Pour qu’il y ait redéfinition, il faut définir une méthode qui possède la même signature d’une méthode de la classe mère, sinon c’est une simple surcharge.
- On peut aussi redéfinir des attributs
Membres protected
d’une classe
Un membre d’une classe appartient à un et un seul des groupes suivants :
public
private
protected
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++
- héritage
public
- Les membres
public
de la classe mère deviennepublic
dans la classe fille - Les membres
protected
de la classe mère devienneprotected
dans la classe fille` - Les membres
private
de la classe mère devienne inaccessible dans la classe fille
- Les membres
- héritage
protected
- Les membres
public
de la classe mère devienneprotected
dans la classe fille - Les membres
protected
de la classe mère devienneprotected
dans la classe fille` - Les membres
private
de la classe mère devienne inaccessible dans la classe fille
- Les membres
- héritage
private
- Les membres
public
de la classe mère devienneprivate
dans la classe fille - Les membres
protected
de la classe mère devienneprivate
dans la classe fille` - Les membres
private
de la classe mère devienne inaccessible dans la classe fille
- Les membres
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 m
“virtual
”.
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.