I. Problème▲
Considérons les deux classes suivantes :
class
B1 {
public
:
virtual
int
ReadBuf( const
char
*
);
// ?
}
;
class
B2 {
public
:
virtual
int
ReadBuf( const
char
*
);
// ?
}
;
Elles sont clairement destinées à être toutes les deux utilisées comme classes de base, mais elles sont sans lien l'une avec l'autre – leurs fonctions ReadBuf sont destinées à faire des choses différentes et les classes proviennent de différents vendeurs de bibliothèques.
Montrez comment écrire une classe D, qui dérive publiquement de B1 et B2 et qui redéfinit les deux fonctions indépendantes ReadBuf pour faire des choses différentes.
II. Solution▲
Montrez comment écrire une classe D, qui dérive publiquement de B1 et B2 et qui redéfinit les deux fonctions indépendantes ReadBuf pour faire des choses différentes.
Voici la tentative naïve qui ne fonctionnera pas :
class
D : public
B1, public
B2 {
public
:
int
ReadBuf( const
char
*
);
// overrides both B1::ReadBuf and B2::ReadBuf
}
;
Elle redéfinit LES DEUX fonctions avec la même implémentation, alors que le but de la question était de redéfinir les deux fonctions pour faire des choses différentes. Vous ne pouvez pas simplement « intervertir » les comportements dans ce D::ReadBuf en fonction de la façon dont il est appelé, parce qu'une fois que vous êtes à l'intérieur de D::ReadBuf, il n'y a aucun moyen de dire quelle interface de base doit être utilisée (si une interface de base a effectivement été utilisée).
II-A. Renommer les fonctions virtuelles▲
Si les deux fonctions dérivées avaient des signatures différentes, il ne devrait pas y avoir de problème : on se contenterait de les redéfinir indépendamment, comme d'habitude. L'astuce, dans ce cas, est de modifier d'une manière ou d'une autre la signature d'au moins une des deux fonctions héritées.
La façon de changer la signature d'une fonction de classe de base consiste à créer une classe intermédiaire qui dérive de la classe de base, à déclarer une nouvelle fonction virtuelle et à forcer la version héritée pour appeler la nouvelle fonction :
class
D1 : public
B1 {
public
:
virtual
int
ReadBufB1( const
char
*
p ) =
0
;
int
ReadBuf( const
char
*
p ) {
// redéfinition de l'héritage
return
ReadBufB1( p ); // pour appeler la nouvelle fonction
}
}
;
class
D2 : public
B2 {
public
:
virtual
int
ReadBufB2( const
char
*
p ) =
0
;
int
ReadBuf( const
char
*
p ) {
// redéfinition de l'héritage
return
ReadBufB2( p ); // pour appeler la nouvelle fonction
}
}
;
D1 et D2 peuvent aussi avoir besoin de dupliquer les constructeurs de B1 et B2 pour que D puisse les invoquer, mais c'est tout. D1 et D2 sont des classes abstraites, alors elles n'ont PAS BESOIN de dupliquer les autres fonctions ou opérateurs de B1 et B2, comme les opérateurs d'affectation.
À présent, nous pouvons simplement écrire :
class
D : public
D1, public
D2 {
public
:
int
ReadBufB1( const
char
*
);
int
ReadBufB2( const
char
*
);
}
;
Les classes dérivées n'ont besoin que de savoir qu'elles ne doivent plus redéfinir ReadBuf lui-même.
III. Remerciements▲
Cet article est une traduction en français par l'équipe de la rubrique C++ de l'article de Herb Sutter publié sur Guru of the Week. Vous pouvez retrouver cet article dans sa version originale sur le site de Guru of the Week : Multiple Inheritance - Part IIIMultiple Inheritance - Part III.
Merci à Luc Hermitte pour sa relecture technique et à _Max_ pour sa relecture orthographique.