web-dev-qa-db-fra.com

Quel est l'intérêt d'une fonction virtuelle pure privée?

Je suis tombé sur le code suivant dans un fichier d'en-tête:

class Engine
{
public:
    void SetState( int var, bool val );
    {   SetStateBool( int var, bool val ); }

    void SetState( int var, int val );
    {   SetStateInt( int var, int val ); }
private:
    virtual void SetStateBool(int var, bool val ) = 0;    
    virtual void SetStateInt(int var, int val ) = 0;    
};

Pour moi, cela signifie que la classe Engine ou une classe qui en dérive doit fournir l'implémentation de ces fonctions virtuelles pures. Mais je ne pensais pas que les classes dérivées pourraient avoir accès à ces fonctions privées afin de les réimplémenter - alors pourquoi les rendre virtuelles?

131
BeeBand

La question dans le sujet suggère une confusion assez commune. La confusion est assez commune, que C++ FAQ déconseillé depuis longtemps d'utiliser des virtuels privés, car la confusion semblait être une mauvaise chose.

Donc, pour éliminer tout d’abord la confusion: oui, les fonctions virtuelles privées peuvent être remplacées dans les classes dérivées. Les méthodes de classes dérivées ne peuvent pas appeler de fonctions virtuelles à partir de la classe de base, mais peuvent leur fournir leur propre implémentation. Selon Herb Sutter, le fait de disposer d'une interface publique non virtuelle dans la classe de base et d'une implémentation privée pouvant être personnalisée dans les classes dérivées permet de mieux "séparer la spécification d'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 une plus grande attention, à mon avis. L'interface publique consiste en 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, il est utile. Le nom est (surprise, surprise!)

"Public non surchargés publics Appelés virtuels non surchargés protégés"

Cela aide à gérer correctement la règle de masquage . Vous pouvez en lire plus à ce sujet ici , mais je vais essayer de l'expliquer sous peu.

Imaginons que les fonctions virtuelles de la classe Engine constituent également son interface et qu’il s’agisse d’un ensemble de fonctions surchargées qui n’est pas purement virtuel. S'ils étaient 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 ints en tant qu'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 seconde 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 membres Engine, la déclaration:

myV8->SetState(5, true);

appellerait void SetState( int var, int val ) à partir de la classe dérivée, en 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 à résoudre et peut simplement écrire

class MyTurbochargedV8 : public Engine
{
private:
    void SetStateInt(int var, int val )  {/*new implementation*/}
};
201
Maciej Hehl

privé virtuel pur la fonction est la base de l'idiome non virtuel (OK, ce n'est pas toujours pure virtuelle, mais toujours virtuelle là-bas). Bien sûr, cela est utilisé pour d’autres choses aussi, mais je trouve cela très utile (: En deux mots: dans une fonction publique, vous pouvez insérer des choses courantes (telles que la journalisation, les statistiques, etc.) au début et à la fin de la fonction et ensuite, "au milieu" pour appeler cette fonction virtuelle privée, qui sera différente pour la classe dérivée spécifique.

class Base
{
    // ..
public:
    void f();
private:
    virtual void DerivedClassSpecific() = 0;
   // ..
};
void Base::f()
{
    //.. Do some common stuff
    DerivedClassSpecific();
    //.. Some other common stuff
}
// ..

class Derived: public Base
{
    // ..
private:
    virtual void DerivedClassSpecific();
    //..
};
void Derived::DerivedClassSpecific()
{
    // ..
}

Pure virtuel - oblige simplement les classes dérivées à l'implémenter.

[~ # ~] edit [~ # ~] : Plus d'informations à ce sujet: Wikipedia :: NVI-idiom

42
Kiril Kirov

D'une part, cela permettrait à une classe dérivée d'implémenter une fonction que la classe de base (contenant la déclaration de fonction virtuelle pure) peut appeler.

17
Michael Goldshteyn

EDIT: Déclarations clarifiées sur la capacité de substitution et la capacité d'accès/d'appel.

Il sera capable de remplacer ces fonctions privées. Par exemple, l'exemple artificiel suivant fonctionne ( EDIT: fait que la méthode de la classe dérivée soit privée et supprime l'invocation de la méthode de la classe dérivée dans main() pour mieux illustrer l'intention du modèle de conception dans utiliser. ):

#include <iostream>

class Engine
{
public:
  void SetState( int var, bool val )
  {
    SetStateBool( var, val );
  }

  void SetState( int var, int val )
  {
    SetStateInt( var, val );
  }

private:

    virtual void SetStateBool(int var, bool val ) = 0;
    virtual void SetStateInt(int var, int val ) = 0;

};

class DerivedEngine : public Engine
{
private:
  virtual void SetStateBool(int var, bool val )
  {
    std::cout << "DerivedEngine::SetStateBool() called" << std::endl;
  }

  virtual void SetStateInt(int var, int val )
  {
    std::cout << "DerivedEngine::SetStateInt() called" << std::endl;
  }
};


int main()
{
  DerivedEngine e;
  Engine * be = &e;

  be->SetState(4, true);
  be->SetState(2, 1000);
}

Les méthodes Privatevirtual d'une classe de base, comme celles de votre code, sont généralement utilisées pour implémenter le modèle modèle de conception de méthode de modèle . Ce modèle de conception permet de modifier le comportement d'un algorithme dans la classe de base sans modifier le code dans la classe de base. Le code ci-dessus, dans lequel les méthodes de classe de base sont appelées via un pointeur de classe de base, est un exemple simple du modèle Template Method.

4
Void

La méthode virtuelle privée est utilisée pour limiter le nombre de classes dérivées pouvant remplacer la fonction donnée. Les classes dérivées qui doivent remplacer la méthode virtuelle privée devront être des amis de la classe de base.

Une brève explication peut être trouvée de DevX.com .


[~ # ~] edit [~ # ~] Une méthode virtuelle privée est effectivement utilisée dans modèle de méthode de modèle . Les classes dérivées peuvent remplacer la méthode virtuelle privée, mais les classes dérivées ne peuvent pas appeler sa méthode virtuelle privée de classe de base (dans votre exemple, SetStateBool et SetStateInt). Seule la classe de base peut effectivement appeler sa méthode virtuelle privée ( uniquement si les classes dérivées doivent appeler l'implémentation de base d'une fonction virtuelle, protégez la fonction virtuelle ).

Un article intéressant peut être trouvé sur Virtuality .

3
Buhake Sindi

Réponse de TL; DR:

Vous pouvez le traiter comme un autre niveau d'encapsulation - quelque part entre protégé et privé : vous ne pouvez pas l'appeler depuis la classe enfant, mais vous pouvez le remplacer.

C'est utile lors de la mise en œuvre de méthode du modèle modèle de conception. Vous pouvez utiliser protégé , mais privé avec virtuel peut être considéré comme un meilleur choix, en raison d'une meilleure encapsulation.

0
jaskmar