La question du sujet suggère une confusion assez courante. La confusion est assez courante, que la FAQ C ++ a prôné pendant longtemps l'utilisation de virtuels privés, car la confusion semblait être une mauvaise chose.
Donc, pour se débarrasser de la confusion en premier: Oui, les fonctions virtuelles privées peuvent être remplacées dans les classes dérivées. Les méthodes des classes dérivées ne peuvent pas appeler des fonctions virtuelles à partir de la classe de base, mais elles peuvent leur fournir leur propre implémentation. Selon Herb Sutter, avoir une interface publique non virtuelle dans la classe de base et une implémentation privée qui peut être personnalisée dans les classes dérivées, permet une meilleure «séparation de la spécification de l'interface de la spécification du comportement personnalisable de l'implémentation». Vous pouvez en savoir plus à ce sujet dans son article "Virtuality" .
Il y a cependant une autre chose intéressante dans le code que vous avez présenté, qui mérite un peu plus d'attention, à mon avis. L'interface publique se compose d'un ensemble de fonctions non virtuelles surchargées et ces fonctions appellent des fonctions virtuelles non publiques et non surchargées. Comme d'habitude dans le monde C ++, c'est un idiome, il a un nom et bien sûr c'est utile. Le nom est (surprise, surprise!)
«Public surchargé non-virtuels virtuels protégés contre les appels non surchargés»
Cela aide à gérer correctement la règle de masquage . Vous pouvez en savoir plus ici , mais je vais essayer de l'expliquer sous peu.
Imaginez que les fonctions virtuelles de la Engine
classe sont aussi son interface et que c'est un ensemble de fonctions surchargées qui ne sont pas purement virtuelles. S'ils étaient purement virtuels, on pourrait toujours rencontrer le même problème, comme décrit ci-dessous, mais plus bas dans la hiérarchie des classes.
class Engine
{
public:
virtual void SetState( int var, bool val ) {/*some implementation*/}
virtual void SetState( int var, int val ) {/*some implementation*/}
};
Supposons maintenant que vous souhaitiez créer une classe dérivée et que vous deviez fournir une nouvelle implémentation uniquement pour la méthode, qui prend deux entiers comme arguments.
class MyTurbochargedV8 : public Engine
{
public:
// To prevent SetState( int var, bool val ) from the base class,
// from being hidden by the new implementation of the other overload (below),
// you have to put using declaration in the derived class
using Engine::SetState;
void SetState( int var, int val ) {/*new implementation*/}
};
Si vous avez oublié de mettre la déclaration using dans la classe dérivée (ou de redéfinir la deuxième surcharge), vous pourriez avoir des problèmes dans le scénario ci-dessous.
MyTurbochargedV8* myV8 = new MyTurbochargedV8();
myV8->SetState(5, true);
Si vous n'avez pas empêché la dissimulation des Engine
membres, la déclaration:
myV8->SetState(5, true);
appellerait à void SetState( int var, int val )
partir de la classe dérivée, convertissant true
en int
.
Si l'interface n'est pas virtuelle et que l'implémentation virtuelle n'est pas publique, comme dans votre exemple, l'auteur de la classe dérivée a un problème de moins à penser et peut simplement écrire
class MyTurbochargedV8 : public Engine
{
private:
void SetStateInt(int var, int val ) {/*new implementation*/}
};