7
votes

Forcer toutes les classes à mettre en œuvre / remplacer une méthode «virtuelle pure» dans la hiérarchie de héritage multi-niveaux

en C ++ Pourquoi le pur Virtual MÉTHODE MANDITES SON OBLIMENT PRÉVRATION SEULEMENT à ses enfants immédiats (pour la création d'objets), mais pas au Grands enfants et ainsi de suite? XXX

Je ne vois pas de gros problème en quittant cette fonctionnalité.
Par exemple, au point de vue de la conception linguistique, il aurait pu être possible que, struct dd est autorisé à utiliser d :: foo uniquement s'il a une déclaration explicite comme < Code> Utiliser D :: FOO; . Sinon, il doit remplacer foo obligatoire.

existe-t-il un moyen pratique d'avoir cet effet en C ++?


6 commentaires

Vous pouvez facturer le foo dans une classe distincte et demander que chaque classe hérite directement de celle-ci (en privé).


Cela ne nécessite rien du genre (sauf si je suis erroné). Il charge que tout objet instancié doit être d'une classe qui n'a pas de méthodes virtuelles indéfinies.


@iammilind: Désolé, je ne suis toujours pas sûr de ce que vous voulez dire. Il n'y a rien de spécial sur les enfants immédiats que les autres descendants. Peut-être un exemple de pourquoi vous pensez que les petits-enfants sont différents.


@Lokiastari, d doit remplacer b :: foo () pour pouvoir créer un objet de type d , mais la même chose n'est pas True pour dd . dd peut toujours être instancié, même s'il ne remplace pas foo () (implicitement il utilise d :: foo () ). Ma question est pourquoi est-ce?


@iammilind: OK. Mais cela n'a rien à voir avec les enfants vs autres descendants. Il force simplement l'un des ancêtres de DD de définir FOO () (si vous l'instanciez), pas l'enfant direct de B.


@Lokiastari, je le sais. Et c'est la question. Pourquoi il n'est pas mandaté à toutes les classes dérivées ultérieures qui souhaitent instancier des objets?


5 Réponses :


2
votes

Dans votre exemple, vous n'avez pas déclaré D :: FOO PURE; C'est pourquoi il n'a pas besoin d'être remplacé. Si vous souhaitez exiger que cela soit remplacé à nouveau, alors déclarez-le pur.

Si vous souhaitez pouvoir instancier d , mais forcez les autres classes dérivées pour remplacer foo , alors vous ne pouvez pas. Cependant, vous pouvez obtenir encore une autre classe de d qui redéclairez-la pure, puis les classes dérivées de que doivent le remplacer à nouveau.


0 commentaires

1
votes

Un pur virtuel signifie que pour être instancié, le pur virtuel doit être remplacé par certains descendant de la classe qui déclare la fonction virtuelle pure. Cela peut être dans la classe d'être instancié ou toute classe intermédiaire entre la base qui déclare le virtuel pur et celui instancié.

Il est toujours possible de disposer de classes intermédiaires qui dérivent d'un avec un virtuel pur sans prouver ce pur virtuel. Comme la classe qui déclare le pur virtuel, ces classes ne peuvent être utilisées que comme des classes basées; Vous ne pouvez pas créer d'instances de ces classes, uniquement de classes qui en dérivent, dans lesquelles chaque virtuel pur a été mis en œuvre.

En ce qui nécessite qu'un descendant remplaçait un virtuel, même si une classe intermédiaire l'a déjà fait, la réponse est non, C ++ ne fournit rien qui est au moins destiné à le faire. Il semble presque que vous puissiez être capable de pirater quelque chose en combinant à l'aide de plusieurs héritages (probablement virtuels) afin que la mise en œuvre de la classe intermédiaire soit présente, mais tenter de l'utiliser serait ambiguë, mais je n'ai pas pensé que suffisamment pour être sûr d'être sûr Comment (ou si) cela fonctionnerait - et même si c'était le cas, cela ne ferait que son tour lorsque vous essayez d'appeler la fonction en question, pas seulement instancier un objet.


1 commentaires

La vraie question est la raison pour laquelle vous voudriez faire cela. Il semble imposer des contraintes inutiles sur la la mise en œuvre des classes dérivées, indésirables; Les contraintes ne concernent que des pré-conditions et des invariances de classe.



2
votes

Qu'est-ce que vous demandez essentiellement, c'est d'exiger que le plus dérivé classe implémente le fonctionnement. Et ma question est: pourquoi? À propos du seul temps que je puisse imaginer que cela soit pertinent est une fonction comme clone () code> ou autre () code>, qui renvoie une nouvelle instance du même type. Et c'est Ce que vous voulez vraiment appliquer, que la nouvelle instance a la même chose taper; même là, où la fonction est réellement mise en œuvre est hors du sujet. Et vous pouvez appliquer que:

class Base
{
    virtual Base* doClone() const = 0;
public:
    Base* clone() const
    {
        Base* results = doClone();
        assert( typeid(*results) == typeid(*this) );
        return results;
    }
}


2 commentaires

Le problème est que la partie après le dernier enfant, qui a mis en œuvre clone () ne sera pas clone () D après avoir appelé clone () sur un pointeur de classe de base - de sorte que certaines données seraient sautées silencieusement. Au moins, ce chèque Typeid est une bonne solution.


@ Nucléaire Le seul problème est que c'est le temps d'exécution et ne compilez pas l'heure. Mieux que rien, je suppose. Mais comme je l'ai dit, je n'ai jamais trouvé que cela soit un vrai problème dans la pratique.



1
votes

Y a-t-il un moyen pratique d'avoir cet effet en C ++?

Non, et pour une bonne raison. Imaginez la maintenance dans un vaste projet si cela faisait partie de la norme. Une classe de base ou une classe de base intermédiaire doit ajouter une interface publique, une interface abstraite. Maintenant, chaque enfant et petit-enfant de celles-ci devront avoir changé et recompanté (même si c'était aussi simple que d'ajouter à l'aide de D :: FOO () comme vous l'avez suggéré), vous voyez probablement où cela se dirige, la cuisine enfer.

Si vous souhaitez vraiment faire respecter la mise en œuvre, vous pouvez forcer la mise en œuvre d'autres virtuels purs dans la ou les catégories d'enfants. Cela peut également être fait en utilisant également le motif CRTP.


3 commentaires

Ma question ne concerne pas les projets existants. Si vous écrivez une classe de base et que vous souhaitez appliquer cela à sa hiérarchie.


Ensuite, utilisez le motif CRTP que vous pouvez le faire. Voyez mon ci-dessus modifier.


@iammilind Vous vous rendez compte que, sauf si vous ne planifiez que du code d'écriture une fois et que vous ne l'avez jamais modifié, il sera à un moment un "projet existant".



8
votes

J'ai trouvé un mécanisme, où au moins nous sommes invités à annoncer la méthode remplacée explicitement em>. Cependant, ce n'est pas le moyen idéal.

Supposons, nous avons peu de méthodes virtuelles code> virtuelles code> dans la base classe B code>: p> xxx pré>

Parmi eux, supposons que nous voulions seulement foo () code> sera remplacé par toute la hiérarchie. Pour la simplicité, nous devons avoir une classe de base code> code>, qui contient cette méthode particulière. Il possède un constructeur de modèle qui accepte simplement le type identique que cette méthode. P> xxx pré>

Chaque classe enfant ultérieure dans la hiérarchie devrait enregistrer em> a foo code> à l'intérieur de son chaque constructeur em> explicitement fort>. E.g.:

struct D : B {
  D () : Register_foo(&D::foo) {}
  virtual void foo () {};
};


0 commentaires