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
fdans une classeBest marquée avec virt-rédacteur de devisfinalet dans une classeDdérivée à partir deBune fonctionD::fremplaceB::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 queoverrideet {[3] } sont indépendants sematically.-
overridesignifie que la fonction remplace une fonction virtuelle dans sa classe de base. Voirf1()etf3(). -
finalsignifie 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 sansvirtualet avecfinal, cela signifie qu'elle remplace déjà une fonction virtuelle dans la classe de base. Dans ce cas, le mot cléoverrideest redondant.Comparez
f4()etf5(). Nous savons que si une fonction membre est déclarée avecvirtualet si ce n'est pas la première fonction virtuelle dans la hiérarchie d'héritage, alors nous devrions utiliseroverridepour 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 quevirtualspé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 queoverridePeut 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évirtualdans la classe de base.
En conclusion , la meilleure pratique à mon avis est la suivante:
-
seulement utiliser
virtualdans la déclaration de la premier fonction virtuelle dans la classe de base; - utilisez toujours
overridepour spécifier la fonction virtuelle classe dérivée, sauf sifinalest é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.