web-dev-qa-db-fra.com

Méthode virtuelle privée en C ++

Quel est l'avantage de rendre une méthode privée virtuelle en C++?

J'ai remarqué cela dans un projet open source C++:

class HTMLDocument : public Document, public CachedResourceClient {
private:
    virtual bool childAllowed(Node*);
    virtual PassRefPtr<Element> createElement(const AtomicString& tagName, ExceptionCode&);
};
114
silverburgh

Herb Sutter l'a très bien expliqué ici .

Directive n ° 2: Préférez rendre les fonctions virtuelles privées.

Cela permet aux classes dérivées de remplacer la fonction pour personnaliser le comportement, sans exposer davantage les fonctions virtuelles directement en les rendant appelables par des classes dérivées (comme cela serait possible si les fonctions étaient simplement protégées). Le fait est que les fonctions virtuelles existent pour permettre la personnalisation; à moins qu’ils aient également besoin d’être invoqués directement à partir du code des classes dérivées, il n’est pas nécessaire de les rendre tout sauf privés.

111
Prasoon Saurav

Si la méthode est virtuelle, elle peut être remplacée par des classes dérivées, même si elle est privée. Lorsque la méthode virtuelle est appelée, la version remplacée est appelée.

(Opposé à Herb Sutter cité par Prasoon Saurav dans sa réponse, le C++ FAQ Lite recommande contre les machines virtuelles privées , principalement parce que cela confond souvent les gens.)

62
sth

Malgré tous les appels pour déclarer un membre virtuel privé, l'argument ne tient pas. Fréquemment, le remplacement d'une fonction virtuelle par une classe dérivée devra appeler la version de la classe de base. Cela ne peut pas s’il est déclaré private:

class Base
{
 private:

 int m_data;

 virtual void cleanup() { /*do something*/ }

 protected:
 Base(int idata): m_data (idata) {}

 public:

 int data() const { return m_data; }
 void set_data (int ndata) { m_data = ndata; cleanup(); }
};

class Derived: public Base
{
 private:
 void cleanup() override
 {
  // do other stuff
  Base::cleanup(); // nope, can't do it
 }
 public:
 Derived (int idata): base(idata) {}
};

Vous devez pour déclarer la méthode de classe de base protected.

Ensuite, vous devez prendre le vilain expédient d’indiquer via un commentaire que la méthode doit être remplacée mais non appelée.

class Base
{
 ...
 protected:
 // chained virtual function!
 // call in your derived version but nowhere else.
 // Use set_data instead
 virtual void cleanup() { /* do something */ }
 ...

Ainsi, la ligne directrice n ° 3 de Herb Sutter ... Mais le cheval est quand même sorti de l'étable.

Lorsque vous déclarez quelque chose protected, vous confiez implicitement le rédacteur de toute classe dérivée pour comprendre et utiliser correctement les éléments internes protégés, exactement comme une déclaration friend implique une plus grande confiance pour private membres.

Les utilisateurs qui adoptent un comportement répréhensible en violant cette confiance (par exemple, étiquetés 'sans intelligence' en ne prenant pas la peine de lire votre documentation) ne peuvent s'en prendre qu'à eux-mêmes.

Mise à jour: Certains commentaires me disent que vous pouvez "chaîner" cette implémentation de fonctions virtuelles à l'aide de fonctions virtuelles privées. Si oui, j'aimerais bien le voir.

Les compilateurs C++ que j'utilise ne laisseront certainement pas une implémentation de classe dérivée appeler une implémentation de classe de base privée.

Si le comité C++ assouplissait les fonctions "privées" pour autoriser cet accès spécifique, je serais entièrement réservé aux fonctions virtuelles privées. Dans l'état actuel des choses, on nous conseille toujours de verrouiller la porte de la grange après le vol du cheval.

11
Spencer

Je suis tombé sur ce concept pour la première fois en lisant 'Effective C++' de Scott Meyers, Point 35: Envisagez des alternatives aux fonctions virtuelles. Je voulais faire référence à Scott Mayers pour les autres personnes qui pourraient être intéressées.

Cela fait partie du modèle de méthode de modèle via l'idiome d'interface non virtuelle: les méthodes destinées au public ne sont pas virtuelles; ils encapsulent plutôt les appels de méthodes virtuelles qui sont privés. La classe de base peut alors exécuter la logique avant et après l'appel de la fonction virtuelle privée:

public:
  void NonVirtualCalc(...)
  {
    // Setup
    PrivateVirtualCalcCall(...);
    // Clean up
  }

Je pense qu'il s'agit d'un modèle de conception très intéressant et je suis sûr que vous pouvez voir à quel point le contrôle ajouté est utile.

  • Pourquoi créer la fonction virtuelle private? La meilleure raison est que nous avons déjà fourni une méthode face à public.
  • Pourquoi ne pas simplement le faire protected afin que je puisse utiliser la méthode pour d’autres choses intéressantes? Je suppose que cela dépendra toujours de votre conception et de la manière dont vous croyez que la classe de base s’intègre. Je dirais que le fabricant de la classe dérivée doit se concentrer sur la mise en œuvre de la logique requise; tout le reste est déjà pris en charge. En outre, il y a la question de l'encapsulation.

Du point de vue du C++, il est tout à fait légitime de remplacer une méthode virtuelle privée même si vous ne pourrez pas l'appeler à partir de votre classe. Cela soutient la conception décrite ci-dessus.

8
Pooven

Je les utilise pour permettre aux classes dérivées de "remplir les blancs" pour une classe de base sans exposer un tel trou aux utilisateurs finaux. Par exemple, j'ai des objets très dynamiques dérivés d'une base commune, qui ne peuvent implémenter que 2/3 de la machine à états globale (les classes dérivées fournissent le tiers restant en fonction d'un argument de modèle, et la base ne peut pas être un modèle pour autres raisons).

J'AI BESOIN d'avoir la classe de base commune pour que bon nombre d'API publiques fonctionnent correctement (j'utilise des modèles variadiques), mais je ne peux pas laisser cet objet entrer dans la nature. Pire, si je laisse les cratères dans la machine à états, sous la forme de fonctions virtuelles pures, n'importe où mais dans "Private", je permets à un utilisateur intelligent ou désemparé issu de l'une de ses classes enfants de remplacer des méthodes qu'il ne doit jamais toucher. J'ai donc mis le cerveau de la machine d'état dans les fonctions virtuelles PRIVATE. Ensuite, les enfants immédiats de la classe de base remplissent les blancs de leurs remplacements NON virtuels et les utilisateurs peuvent utiliser en toute sécurité les objets résultants ou créer leurs propres classes dérivées sans se soucier de tout endommager de la machine à états.

En ce qui concerne l'argument selon lequel vous ne devriez pas avoir des méthodes virtuelles publiques, je dis BS. Les utilisateurs peuvent remplacer les virtual virtuels de manière incorrecte tout aussi facilement que les virtuels - ils définissent de nouvelles classes après tout. Si le public ne doit pas modifier une API donnée, ne la rendez pas virtuelle AT ALL dans des objets accessibles au public.

3
Zack Yezek