Faculté Informatique & Communications Cours d'informatique |
Nous avons vu que la notion d'héritage permet de dériver des classes plus spécialisées (sous-classes) à partir de classes préexistantes (super-classes), implémentant ainsi la relation «est-un». Qu'en est-il lorsque l'on souhaite dériver une sous-classe de plusieurs super-classes à la fois ?
Cela s'appelle «l'héritage multiple». Il faut savoir que l'héritage multiple n'existe pas dans tous les langages orientés objets, mais il est possible en C++. Cela se fait simplement en ajoutant plusieurs classe dans la partie héritage. Par exemple :
class Ovovivipare : public Ovipare, public Vivipare { /* définition des attributs et des méthodes * spécifiques à la sous-classe */ ... };
A priori aucune nouvelle difficulté donc, si ce n'est de bien faire attention au fait que l'ordre est important. Dans l'exemple précédent, un Ovovivipare est avant tout un Ovipare et est aussi (ensuite) un Vivipare. Ceci importe dans l'ordre d'appel des constructeurs et des destructeurs.
Un souci pratique intervient cependant lorsque l'on souhaite accéder à un membre (i.e. attribut ou méthode) qui existe avec le même nom dans plusieurs des super-classes : lequel appeler et comment l'appeler ?
Ce serait par exemple le cas dans l'exemple précédent si les classes Ovipare et Vivipare avaient chacune une méthode afficher().
Il y a trois moyens pour cela :
Ovovivipare requin; requin.Vivipare::afficher();
class Ovovivipare : public Ovipare, public Vivipare { ... using Vivipare::afficher; };
class Ovovivipare : public Ovipare, public Vivipare { ... void afficher() const; }; ... void Ovovivipare::afficher () const { ... }
Un autre souci, plus sérieux car conceptuel, intervient lorsqu'une classe hérite de deux classes (héritage multiple) lesquelles héritent de la même classe commune. Cette situation est illustrée par l'un ou l'autre des diagrammes suivants :
Quel héritage ? |
---|
La question de conception à se poser ici est de savoir si la classe la plus spécialisée (Ovovivipare dans l'exemple précédent) est une (figure de droite) ou plusieurs (figure de gauche) réalisation(s) de la super-classe héritée plusieurs fois.
Dans l'exemple précédent, on peut sans crainte, je crois, pouvoir affirmer qu'un Ovovivipare est un Animal. C'est donc le schéma de droite que l'on souhaite.
Dans ce cas on dit que la classe Animal est «virtuelle». Ce terme, à mon avis mal choisi, veut juste dire que la classe ne sera présente qu'une seule fois en cas d'héritage multiple via des classes intermédiaires. C'est en fait plus l'héritage (via certaines des classes intermédiaires) que la classe elle même qui est virtuel !
Un tel héritage (qui donc dans notre exemple doit se déclarer au niveau de Ovipare (et de Vivipare) pour un futur potentiel Ovovivipare : beurk !!) s'écrit :
class Vivipare : public virtual Animal {...}; class Ovipare : public virtual Animal {...};
Mais ATTENTION alors :
dans une dérivation virtuelle, la super-classe virtuelle doit être initialisée
par sa classe la plus dérivée.
La sous-classe la plus dérivée doit donc effectuer
elle-même un appel au constructeur de la super-classe virtuelle :
Ovovivipare::Ovovivipare(...) : Animal(...), Ovipare(...), ... {...}
Note : on pourrait aussi imaginer des situations (plus rares car dans la plupart des cas je préférerais alors utiliser l'encapsulation : «possède» plutôt que «est-un». Si vous me trouvez un bon exemple je suis preneur !) où on n'a pas besoin d'héritage (pardon, de classe) virtuel(le) et où la présence des deux super-classes est conceptuellement correcte (figure de gauche ci-dessus). Dans un tel cas, pas besoin des «virtual», les deux instances seront alors bien présentes.