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).
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.