Implémentation d’une fonction virtuelle pure à partir d’une classe de base abstraite: le spécificateur de substitution a-t-il un sens?

Contexte

Je viens de trébucher sur un cas d’utilisation du spécificateur d’ override qui, autant que je sache, semble redondant et sans signification sémantique particulière, mais peut-être me manque-t-il quelque chose, d’où cette question. Avant de poursuivre, je tiens à préciser que j’ai essayé de trouver une réponse à cette question ici sur SO, mais les discussions les plus proches que j’ai eues ont été les suivantes, sans vraiment répondre à ma question question).

  • C ++ Virtual / Pure Virtual Explained
  • C ++ substitue la méthode virtuelle pure avec la méthode virtuelle pure

Question

Considérez la classe abstraite suivante:

 struct Abstract { virtual ~Abstract() {}; virtual void foo() = 0; }; 

Existe-t-il une raison d’utiliser le spécificateur de override lors de l’implémentation de foo() dans une classe non abstraite dérivée directement de Abstract (comme dans DerivedB ci-dessous)? C’est-à-dire, quand l’implémentation de foo() est déjà requirejse pour que la classe dérivée soit non abstraite (et n’annule pas vraiment quoi que ce soit)?

 /* "common" derived class implementation, in my personal experience (include virtual keyword for semantics) */ struct DerivedA : public Abstract { virtual void foo() { std::cout << "A foo" << std::endl; } }; /* is there any reason for having the override specifier here? */ struct DerivedB : public Abstract { virtual void foo() override { std::cout << "B foo" << std::endl; } }; 

Je ne suis pas un grand partisan du override , mais, en supposant que vous trouviez cela utile en général, il est utile de remplacer un rôle par une fonction virtuelle qui remplace une fonction virtuelle pure. Considérez cet exemple plutôt artificiel:

 struct Base { virtual void f() = 0; }; struct Derived : Base { virtual void f(); virtual void f(int); }; 

Supposons maintenant que le responsable de la Base (peut-être même votre futur individu) change de Base pour ressembler à ceci:

 struct Base { virtual void f(int) = 0; }; 

Maintenant, le comportement de Derived a discrètement changé. Avec le override le compilateur signalerait une erreur.

Techniquement, les deux versions sont syntaxiquement correctes et légales. Le spécificateur de override est principalement destiné à empêcher un comportement inattendu. Le compilateur émettra une erreur dès qu’il rencontrera une fonction membre marquée comme override ce qui ne override pas réellement une fonction virtuelle. Cela peut se produire si, pour une raison quelconque, vous modifiez la signature de la fonction de classe de base virtuelle. Considérons cet exemple:

 class Abstract { virtual void foo() { ...} }; class Derived : public Abstract { void foo() override { ... } }; 

Maintenant, si la signature de Abstract::foo est modifiée, disons

 class Abstract { virtual void foo(int bar) { ...} }; 

le compilateur lancera une erreur sur Derived::foo car il ne remplace plus une fonction de Abstract qu’il ne ferait pas sans le qualificatif de override . Cela vous aide à mieux gérer votre code. Cependant, dans votre cas spécifique (c’est-à-dire avec des déclarations virtuelles pures), une erreur serait également générée. Donc, l’utilisation de la override est principalement considérée comme une “bonne pratique”, je suppose. Plus d’informations sur ce sujet: http://fr.cppreference.com/w/cpp/language/override

Dans le cas de fonctions virtuelles pures et de compilations, pas vraiment. Vous obtiendrez quand même une erreur (sauf comme dans l’exemple de Pete)

Mais le message d’erreur peut être plus lisible si vous obtenez une erreur du type ” votre fonction ne remplace rien ” par rapport à plus tard ” ne peut pas instancier une classe abstraite

Un autre avantage serait en lisant la déclaration que vous savez qu’il s’agit d’une méthode dérivée d’une classe de base.

De plus, il est bon de s’habituer à déclarer toutes les méthodes remplacées par override . Alors, pourquoi faire une différence ici et avoir un style incohérent.

Pour expliquer pourquoi il est bon de déclarer toutes les méthodes override :

imaginez que vous avez

 class A { virtual void Foo(); }; class B: public A { virtual void Foo() override; }; 

Et puis vous changez Foo en une fonction const dans A Sans override cela comstackra mais lorsque vous appelez A->foo() et que c’est un object B, B->foo() ne sera pas appelé sans aucune indication que quelque chose y a changé. Avec override vous obtenez une erreur ici.

Le principal avantage de la override ici serait d’encourager la maintenabilité. Considérez ci-dessous:

 class Foo { public: virtual void foo() = 0; }; class Derived : public Foo { public: //.... virtual void foo(double x) override { //This throws error } }; 

Comme vous pouvez le voir ci-dessus, le compilateur émettrait une erreur si vous compiliez ce qui précède. Ce qui arriverait, c’est que le compilateur se plaint de ce que la fonction n’a pas la même signature Sans le mot clé de override , le résultat aurait été différent.