Est-ce que final implique le dépassement?
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 s'il n'y a pas de méthode de base correspondante trouvée.
Ma compréhension du mot-clé final
est qu'il indique au compilateur qu'aucune classe ne doit remplacer cette fonction virtual
.
Est Donc override final
redondant? Il semble bien compiler . Quelles informations override final
véhiculent que final
ne transmet pas? Quel est le cas d'utilisation d'une telle combinaison?
5 réponses
final
ne nécessite pas que la fonction remplace quoi que ce soit en premier lieu. Son effet est défini dans [classe.virtuel]/4
Si une fonction virtuelle
f
dans une classeB
est marquée avec virt-rédacteur de devisfinal
et dans une classeD
dérivée à partir deB
une fonctionD::f
remplaceB::f
, le programme est mal formé.
C'est ça. Maintenant {[10] } signifierait simplement
"Cette fonction remplace une classe de base one (override
) et ne peut pas être remplacée lui-même (final
)."final
en soi, cela imposerait une exigence plus faible.
override
et {[2] } ont un comportement indépendant.
Notez que final
ne peut être utilisé que pour les fonctions virtuelles - [class.mem] / 8
Un virt-spécificateur-seq doit apparaître que dans la déclaration d'un fonction de membre virtuel (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 virtuelle, c'est-à-dire quand foo
remplace une fonction virtuelle appelée foo
dans une classe de base, ce qui rend foo
automatiquement virtuel. ainsi {[11] } 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 remplacée. Il est parfaitement valide (s'il a une valeur quelque peu douteuse) de déclarer une fonction virtuelle comme final
sur sa première déclaration dans la hiérarchie d'héritage.
Une raison pour laquelle je peux penser à créer une fonction virtuelle et immédiatement finale est si vous voulezempêcher une classe dérivée de donner le même nom et les paramètres une signification différente.
(Passez à la fin pour voir la conclusion si vous êtes pressé.)
Les Deux override
et final
ne peuvent apparaître que dans une déclaration dans une fonction virtuelle. Et les deux mots clés peuvent être utilisés dans la même déclaration de fonction, mais s'il est utile de les utiliser tous les deux 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()
etf6()
. Nous savons queoverride
et {[3] } sont indépendants sematically.-
override
signifie que la fonction remplace une fonction virtuelle dans sa classe de base. Voirf1()
etf3()
. -
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.) Voirf6()
etf4()
.
-
Comparez
f2()
etf3()
. Nous savons que si une fonction membre est déclarée sansvirtual
et avecfinal
, cela signifie qu'elle remplace déjà une fonction virtuelle dans la classe de base. Dans ce cas, le mot cléoverride
est redondant.Comparez
f4()
etf5()
. Nous savons que si une fonction membre est déclarée avecvirtual
et si ce n'est pas la première fonction virtuelle dans la hiérarchie d'héritage, alors nous devrions utiliseroverride
pour spécifier la relation de remplacement. Sinon, nous pouvons accidentellement ajouter une nouvelle fonction virtuelle dans la classe dérivée.Comparez
f1()
etf7()
. Nous savons que toute fonction de membre, pas seulement ceux virtuels, peut être remplacé dans la classe dérivée. Ce quevirtual
spécifie est polymorphisme, ce qui signifie que la décision quant à la fonction à exécuter est retardée jusqu'à l'exécution et non de la compilation. (Cela devrait être évité dans la pratique.)Comparez
f7()
etf8()
. Nous savons que nous pouvons même remplacer une fonction de classe de base et en faire une nouvelle fonction virtuelle. (Ce qui signifie que toute fonction membref8()
de classe dérivée de {[29] } sera virtuelle.) (Cela devrait être évité dans la pratique trop.)Comparez
f7()
etf9()
. Nous savons queoverride
Peut nous aider à trouver l'erreur lorsque nous voulons remplacer une fonction virtuelle dans la classe dérivée tout en oubliant d'ajouter le mot clévirtual
dans la classe de base.
En conclusion , la meilleure pratique à mon avis est la suivante:
-
seulement utiliser
virtual
dans la déclaration de la premier fonction virtuelle dans la classe de base; - utilisez toujours
override
pour spécifier la fonction virtuelle classe dérivée, sauf sifinal
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 une substitution de cette fonction.
Le mot clé override
est important en ce sens qu'il impose que vous surchargiez effectivement une fonction virtuelle (au lieu de déclarer une nouvelle fonction non liée). Voir ce post concernant l'override
Si longue histoire courte, ils ont chacun servir leur propre usage particulier, et il est souvent correct d'utiliser les deux.
Le code suivant (avec le spécificateur final
) est compilé. Mais la compilation échoue lorsque final
est remplacé par override final
. Ainsi, 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
dit que cette méthode ne peut pas être remplacée dans toute 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 de remplacement de la classe de base.