Si je comprends bien, le mot clé override
indique qu'une déclaration donnée implémente une méthode de base virtual
, et la compilation devrait échouer si aucune méthode de base correspondante n'est trouvée.
Ma compréhension du mot clé final
est qu'il indique au compilateur qu'aucune classe ne doit remplacer cette fonction virtual
.
override final
Est-il donc redondant? Il semble bien compiler . Quelles informations override final
Transmet-elles que final
ne transmet pas? Quel est le cas d'utilisation d'une telle combinaison?
final
n'exige pas que la fonction écrase quoi que ce soit en premier lieu. Son effet est défini dans [class.virtual]/4 comme
Si une fonction virtuelle
f
dans une classeB
est marquée avec le virt-specifierfinal
et dans une classeD
dérivée deB
une fonctionD::f
remplaceB::f
, le programme est mal formé.
C'est ça. Maintenant override final
signifierait simplement
„Cette fonction remplace une classe de base (override
) et ne peut pas être remplacée elle-même (final
).“final
à lui seul imposerait une exigence plus faible. override
et final
ont un comportement indépendant.
Notez que final
ne peut être utilisé que pour les fonctions virtuelles - [class.mem]/8
Un virt-specifier-seq ne doit apparaître que dans la déclaration d'une fonction membre virtuelle (10.3).
D'où la déclaration
void foo() final;
Est effectivement le même que
virtual void foo() final override;
Puisque les deux nécessitent foo
pour remplacer quelque chose - la deuxième déclaration en utilisant override
, et la première en étant valide si et seulement si foo
est implicitement virtuel, c'est-à-dire lorsque foo
remplace une fonction virtuelle appelée foo
dans une classe de base, ce qui rend foo
dans la dérivée automatiquement virtuelle. Ainsi override
serait superflu dans les déclarations où final
, mais pas virtual
, se produit.
Pourtant, cette dernière déclaration exprime l'intention beaucoup plus clairement et devrait certainement être préférée.
final
n'implique pas nécessairement que la fonction est surchargée. Il est parfaitement valide (si sa valeur est quelque peu douteuse) de déclarer une fonction virtuelle comme final
sur sa déclaration first dans la hiérarchie d'héritage.
Une des raisons pour lesquelles je peux penser à créer une fonction virtuelle et immédiatement finale est si vous voulez empêcher une classe dérivée de donner au même nom et paramètres une signification différente.
(Passez à la fin pour voir la conclusion si vous êtes pressé.)
override
et final
ne peuvent apparaître que dans la déclaration d'une fonction virtuelle. Et les deux mots clés peuvent être utilisés dans la même déclaration de fonction, mais leur utilité dépend des situations.
Prenez le code suivant comme exemple:
#include <iostream>
using std::cout; using std::endl;
struct B {
virtual void f1() { cout << "B::f1() "; }
virtual void f2() { cout << "B::f2() "; }
virtual void f3() { cout << "B::f3() "; }
virtual void f6() final { cout << "B::f6() "; }
void f7() { cout << "B::f7() "; }
void f8() { cout << "B::f8() "; }
void f9() { cout << "B::f9() "; }
};
struct D : B {
void f1() override { cout << "D::f1() "; }
void f2() final { cout << "D::f2() "; }
void f3() override final { cout << "D::f3() "; } // need not have override
// should have override, otherwise add new virtual function
virtual void f4() final { cout << "D::f4() "; }
//virtual void f5() override final; // Error, no virtual function in base class
//void f6(); // Error, override a final virtual function
void f7() { cout << "D::f7() "; }
virtual void f8() { cout << "D::f8() "; }
//void f9() override; // Error, override a nonvirtual function
};
int main() {
B b; D d;
B *bp = &b, *bd = &d; D *dp = &d;
bp->f1(); bp->f2(); bp->f3(); bp->f6(); bp->f7(); bp->f8(); bp->f9(); cout << endl;
bd->f1(); bd->f2(); bd->f3(); bd->f6(); bd->f7(); bd->f8(); bd->f9(); cout << endl;
dp->f1(); dp->f2(); dp->f3(); dp->f6(); dp->f7(); dp->f8(); dp->f9(); cout << endl;
return 0;
}
La sortie est
B::f1() B::f2() B::f3() B::f6() B::f7() B::f8() B::f9()
D::f1() D::f2() D::f3() B::f6() B::f7() B::f8() B::f9()
D::f1() D::f2() D::f3() B::f6() D::f7() D::f8() B::f9()
Comparez f1()
et f6()
. Nous savons que override
et final
est indépendant de façon sémantique.
override
signifie que la fonction remplace une fonction virtuelle dans sa classe de base. Voir f1()
et f3()
.final
signifie que la fonction ne peut pas être remplacée par sa classe dérivée. (Mais la fonction elle-même n'a pas besoin de remplacer une fonction virtuelle de classe de base.) Voir f6()
et f4()
.Comparez f2()
et f3()
. Nous savons que si une fonction membre est déclarée sans virtual
et avec final
, cela signifie qu'elle remplace déjà une fonction virtuelle dans la classe de base. Dans ce cas, la clé Word override
est redondante.
Comparez f4()
et f5()
. Nous savons que si une fonction membre est déclarée avec virtual
et si ce n'est pas la première fonction virtuelle dans la hiérarchie d'héritage, alors nous devons utiliser override
pour spécifier la relation de remplacement. Sinon, nous pouvons accidentellement ajouter une nouvelle fonction virtuelle dans une classe dérivée.
Comparez f1()
et f7()
. Nous savons que toute fonction membre, pas seulement virtuelle, peut être remplacée dans une classe dérivée. Ce que virtual
spécifie est le polymorphisme , ce qui signifie que la décision quant à la fonction à exécuter est retardée jusqu'à l'exécution au lieu de la compilation. (Cela devrait être évité dans la pratique.)
Comparez f7()
et f8()
. Nous savons que nous pouvons même remplacer une fonction de classe de base et en faire une nouvelle virtuelle. (Ce qui signifie que toute fonction membre f8()
de la classe dérivée de D
sera virtuelle.) (Cela devrait également être évité dans la pratique.)
Comparez f7()
et f9()
. Nous savons que override
peut nous aider à trouver l'erreur lorsque nous voulons remplacer une fonction virtuelle dans la classe dérivée tout en oubliant d'ajouter la clé Word virtual
dans la classe de base.
En conclusion, la meilleure pratique à mon avis est:
virtual
dans la déclaration du premier virtuel fonction dans la classe de base;override
pour spécifier la fonction virtuelle de substitution dans la classe dérivée, sauf si final
est également spécifié.Non final
n'implique pas nécessairement override
. En fait, vous pouvez déclarer une fonction virtual
que vous déclarez immédiatement final
voir ici . Le mot clé final
indique simplement qu'aucun class
dérivé ne peut créer un remplacement de cette fonction.
Le mot clé override
est important dans la mesure où il impose que vous remplaciez effectivement une fonction virtuelle (au lieu d'en déclarer une nouvelle sans rapport). Voir ce post concernant override
Donc, pour faire court, chacun d'eux sert son propre but particulier, et il est souvent correct d'utiliser les deux.
Le code suivant (avec le spécificateur final
) se compile. Mais la compilation échoue lorsque final
est remplacé par override final
. Donc override final
transmet plus d'informations (et empêche la compilation) que simplement final
.
class Base
{
public:
virtual ~Base() {}
};
class Derived : public Base
{
public:
virtual void foo() final
{
std::cout << "in Derived foo\n";
}
};
Essentiellement, override final
indique que cette méthode ne peut être remplacée dans aucune classe dérivée et cette méthode remplace une méthode virtuelle dans une classe de base. final
seul ne spécifie pas la partie prioritaire de la classe de base.