Je sais que ce final
mot - clé est utilisé pour éviter que la méthode virtuelle ne soit remplacée par les classes dérivées. Cependant, je ne trouve aucun exemple utile quand je devrais vraiment utiliser un final
mot-clé avec une virtual
méthode. De plus, il semble que l'utilisation de final
méthodes virtuelles soit une mauvaise odeur car elle interdit aux programmeurs d'étendre la classe à l'avenir.
Ma question est la suivante:
Existe-t-il un cas utile où je devrais vraiment utiliser final
dans la virtual
déclaration de méthode?
Réponses:
Un récapitulatif de ce que fait le
final
mot-clé: Disons que nous avons une classe de baseA
et une classe dérivéeB
. La fonctionf()
peut être déclarée enA
tant quevirtual
, ce qui signifie que la classeB
peut la remplacer. Mais alors, la classeB
pourrait souhaiter que toute classe qui en est dérivéeB
ne soit pas en mesure de remplacerf()
. C'est quand nous avons besoin de déclarerf()
commefinal
dansB
. Sans lefinal
mot - clé, une fois que nous avons défini une méthode comme virtuelle, toute classe dérivée serait libre de la remplacer. Lefinal
mot-clé est utilisé pour mettre fin à cette liberté.Un exemple de la raison pour laquelle vous auriez besoin d'une telle chose: Supposons que la classe
A
définit une fonction virtuelleprepareEnvelope()
, et la classe laB
remplace et l'implémente comme une séquence d'appels à ses propres méthodes virtuellesstuffEnvelope()
,lickEnvelope()
etsealEnvelope()
. La classe a l'B
intention de permettre aux classes dérivées de remplacer ces méthodes virtuelles pour fournir leurs propres implémentations, mais la classeB
ne veut pas permettre à une classe dérivée de remplacerprepareEnvelope()
et donc de changer l'ordre des éléments, de lécher, de sceller ou d'omettre d'invoquer l'une d'entre elles. Donc, dans ce cas, la classeB
déclareprepareEnvelope()
comme finale.la source
class B might wish that any class which is further derived from B should not be able to override f()
? Existe-t-il un cas réel où une telle classe B et une telle méthode f () existent et s’adaptent bien?Il est souvent utile du point de vue de la conception de pouvoir marquer les choses comme immuables. De la même manière, le
const
compilateur fournit des protections et indique qu'un état ne doit pas changer,final
peut être utilisé pour indiquer que le comportement ne doit pas changer plus bas dans la hiérarchie d'héritage.Exemple
Imaginez un jeu vidéo où des véhicules conduisent le joueur d'un endroit à un autre. Tous les véhicules doivent vérifier pour s'assurer qu'ils se rendent à un endroit valide avant le départ (en s'assurant que la base à l'emplacement n'est pas détruite, par exemple). Nous pouvons commencer par utiliser l'idiome d'interface non virtuelle (NVI) pour garantir que cette vérification est effectuée quel que soit le véhicule.
Maintenant, disons que nous avons des véhicules volants dans notre jeu, et quelque chose que tous les véhicules volants ont besoin et ont en commun, c'est qu'ils doivent passer un contrôle d'inspection de sécurité à l'intérieur du hangar avant le décollage.
Ici, nous pouvons utiliser
final
pour garantir que tous les véhicules volants subiront une telle inspection et communiquer également cette exigence de conception des véhicules volants.En utilisant
final
de cette manière, nous étendons effectivement la flexibilité de l'idiome d'interface non virtuelle pour fournir un comportement uniforme dans la hiérarchie d'héritage (même après coup, pour contrer le problème de la classe de base fragile) aux fonctions virtuelles elles-mêmes. De plus, nous nous achetons une marge de manœuvre pour effectuer des changements centraux qui affectent tous les types de véhicules volants après coup sans modifier chaque implémentation de véhicule volant qui existe.Ceci est un exemple d'utilisation
final
. Il y a des contextes que vous rencontrerez où il est tout simplement insensé qu'une fonction de membre virtuel soit outrepassée davantage - cela pourrait entraîner une conception fragile et une violation de vos exigences de conception.C'est là qu'il
final
est utile d'un point de vue design / architectural.Il est également utile du point de vue d'un optimiseur car il fournit à l'optimiseur ces informations de conception qui lui permettent de dévirtualiser les appels de fonction virtuels (en éliminant la surcharge de répartition dynamique, et souvent de manière plus significative, en éliminant une barrière d'optimisation entre l'appelant et l'appelé).
Question
D'après les commentaires:
Il n'est pas logique pour une classe de base à la racine d'une hiérarchie de déclarer une fonction à la fois
virtual
etfinal
. Cela me semble assez idiot, car cela obligerait à la fois le compilateur et le lecteur humain à sauter à travers des cerceaux inutiles qui peuvent être évités en évitant simplementvirtual
carrément dans un tel cas. Cependant, les sous-classes héritent des fonctions de membre virtuel comme ceci:Dans ce cas, l'
Bar::f
utilisation explicite ou non du mot-clé virtuelBar::f
est une fonction virtuelle. Levirtual
mot-clé devient alors facultatif dans ce cas. Il peut donc être judicieux deBar::f
spécifier asfinal
, même s'il s'agit d'une fonction virtuelle (final
ne peut être utilisée que pour des fonctions virtuelles).Et certaines personnes peuvent préférer, stylistiquement, indiquer explicitement que
Bar::f
c'est virtuel, comme ceci:Pour moi, c'est un peu redondant d'utiliser les deux
virtual
et lesfinal
spécificateurs pour la même fonction dans ce contexte (de mêmevirtual
etoverride
), mais c'est une question de style dans ce cas. Certaines personnes pourraient trouver quevirtual
communique quelque chose de précieux ici, un peu comme utiliserextern
pour les déclarations de fonction avec liaison externe (même si c'est facultatif sans autres qualificatifs de liaison).la source
private
méthode dans votreVehicle
classe? Tu ne voulais pas dire à laprotected
place?private
ne s'étend pas à la surenchère (un peu contre-intuitive). Les spécificateurs publics / protégés / privés pour les fonctions virtuelles ne s'appliquent qu'aux appelants et non aux remplaçants, en gros. Une classe dérivée peut remplacer les fonctions virtuelles de sa classe de base quelle que soit sa visibilité.protected
pourrait avoir un sens un peu plus intuitif. Je préfère simplement atteindre la visibilité la plus faible possible chaque fois que je le peux. Je pense que la raison pour laquelle le langage est conçu de cette manière est que, sinon, les fonctions de membre virtuel privé n'auraient aucun sens en dehors du contexte de l'amitié, car aucune classe mais un ami ne pourrait alors les remplacer si les spécificateurs d'accès importaient dans le contexte de passer outre et pas seulement d'appeler.protected
pour éviter de dérouter les autres. J'ai fini par mettre un commentaire, au moins, décrivant comment les fonctions virtuelles privées peuvent toujours être remplacées.Il permet de nombreuses optimisations, car il peut être connu au moment de la compilation quelle fonction est appelée.
Soyez prudent en jetant le mot «odeur de code» autour. "final" ne rend pas impossible l'extension de la classe. Double-cliquez sur le mot «final», appuyez sur retour arrière et étendez la classe. CEPENDANT final est une excellente documentation que le développeur ne s'attend pas à ce que vous remplaciez la fonction, et qu'à cause de cela le prochain développeur devrait être très prudent, car la classe pourrait cesser de fonctionner correctement si la méthode finale est remplacée de cette façon.
la source
final
etvirtual
serait utilisé en même temps?final
etoverride
.final
peut vraiment les aider