Ich weiß, dass das final
Schlüsselwort verwendet wird, um zu verhindern, dass virtuelle Methoden von abgeleiteten Klassen überschrieben werden. Ich kann jedoch kein nützliches Beispiel finden, wenn ich wirklich ein final
Schlüsselwort mit virtual
Methode verwenden sollte. Darüber hinaus scheint die Verwendung final
mit virtuellen Methoden ein schlechter Geruch zu sein, da es Programmierern nicht erlaubt, den Unterricht in Zukunft zu erweitern.
Meine Frage ist als nächstes:
Gibt es einen nützlichen Fall, den ich wirklich final
in der virtual
Methodendeklaration verwenden sollte?
Antworten:
Eine Zusammenfassung dessen, was das
final
Schlüsselwort bewirkt: Nehmen wir an, wir haben eine BasisklasseA
und eine abgeleitete KlasseB
. Die Funktionf()
kann als deklariertA
werdenvirtual
, was bedeutet, dass die KlasseB
sie überschreiben kann. Aber dannB
könnte die Klasse wünschen, dass jede Klasse, von der weiter abgeleitet wird,B
nicht überschrieben werden kannf()
. Das ist , wenn wir erklären müssen ,f()
wiefinal
inB
. Ohne dasfinal
Schlüsselwort kann jede abgeleitete Klasse eine Methode überschreiben, sobald wir sie als virtuell definieren. Dasfinal
Schlüsselwort wird verwendet, um dieser Freiheit ein Ende zu setzen.Ein Beispiel dafür , warum Sie so etwas brauchen würde: Angenommen Klasse
A
definiert eine virtuelle FunktionprepareEnvelope()
und KlasseB
überschreibt sie und setzt sie als Folge von Aufrufen zu seinen eigenen virtuellen MethodenstuffEnvelope()
,lickEnvelope()
undsealEnvelope()
. Die KlasseB
beabsichtigt, abgeleiteten Klassen das Überschreiben dieser virtuellen Methoden zu ermöglichen, um ihre eigenen Implementierungen bereitzustellen. Die KlasseB
möchte jedoch nicht zulassen, dass abgeleitete Klassen überschreibenprepareEnvelope()
und somit die Reihenfolge der Inhalte ändern, lecken, versiegeln oder das Aufrufen einer dieser Methoden unterlassen. In diesem Fall wird die Klasse als endgültigB
deklariertprepareEnvelope()
.quelle
class B might wish that any class which is further derived from B should not be able to override f()
? Gibt es einen realen Fall, in dem eine solche Klasse B und Methode f () existiert und gut passt?Aus gestalterischer Sicht ist es oft nützlich, Dinge als unveränderlich markieren zu können. Auf die gleiche Weise
const
bietet der Compiler Schutz und zeigt an, dass sich ein Status nicht ändern sollte. Erfinal
kann verwendet werden, um anzuzeigen, dass sich das Verhalten in der Vererbungshierarchie nicht weiter ändern sollte.Beispiel
Stellen Sie sich ein Videospiel vor, bei dem Fahrzeuge den Spieler von einem Ort zum anderen bringen. Alle Fahrzeuge sollten vor dem Abflug überprüfen, ob sie zu einem gültigen Ort fahren (z. B. um sicherzustellen, dass die Basis am Ort nicht zerstört wird). Wir können zunächst die nicht-virtuelle Schnittstellensprache (NVI) verwenden, um sicherzustellen, dass diese Prüfung unabhängig vom Fahrzeug durchgeführt wird.
Nehmen wir jetzt an, wir haben fliegende Fahrzeuge in unserem Spiel, und alle fliegenden Fahrzeuge benötigen und haben gemeinsam, dass sie vor dem Start eine Sicherheitsüberprüfung im Hangar durchlaufen müssen.
Hier können wir
final
sicherstellen, dass alle fliegenden Fahrzeuge eine solche Inspektion durchlaufen, und auch diese Konstruktionsanforderungen von fliegenden Fahrzeugen kommunizieren.Auf
final
diese Weise erweitern wir effektiv die Flexibilität der nicht-virtuellen Schnittstellensprache, um ein einheitliches Verhalten entlang der Vererbungshierarchie (auch nachträglich, um dem fragilen Basisklassenproblem entgegenzuwirken) auf die virtuellen Funktionen selbst bereitzustellen. Darüber hinaus kaufen wir uns Spielraum, um nachträglich zentrale Änderungen vorzunehmen, die sich auf alle Flugfahrzeugtypen auswirken, ohne jede vorhandene Flugfahrzeugimplementierung zu ändern.Dies ist ein Beispiel für die Verwendung
final
. Es gibt Kontexte, in denen es einfach nicht sinnvoll ist, eine virtuelle Elementfunktion weiter zu überschreiben. Dies kann zu einem spröden Design und einer Verletzung Ihrer Designanforderungen führen.Hier
final
ist es aus gestalterischer / architektonischer Sicht nützlich.Dies ist auch aus Sicht des Optimierers nützlich, da es dem Optimierer diese Entwurfsinformationen zur Verfügung stellt, die es ihm ermöglichen, virtuelle Funktionsaufrufe zu devirtualisieren (wodurch der dynamische Versandaufwand beseitigt wird und häufig eine Optimierungsbarriere zwischen Anrufer und Angerufenen beseitigt wird).
Frage
Aus den Kommentaren:
Es ist nicht sinnvoll, dass eine Basisklasse an der Wurzel einer Hierarchie eine Funktion als beide
virtual
und deklariertfinal
. Das kommt mir ziemlich albern vor, da sowohl der Compiler als auch der menschliche Leser durch unnötige Reifen springen müssten, was vermieden werden kann, indemvirtual
in einem solchen Fall einfach alles vermieden wird. Unterklassen erben jedoch Funktionen für virtuelle Elemente wie folgt:In diesem Fall ist es eine virtuelle Funktion , ob
Bar::f
das virtuelle Schlüsselwort explizit verwendet wird oder nichtBar::f
. Dasvirtual
Schlüsselwort wird dann in diesem Fall optional. Daher kann es sinnvoll seinBar::f
, als angegeben zu werdenfinal
, obwohl es sich um eine virtuelle Funktion handelt (final
kann nur für virtuelle Funktionen verwendet werden).Und manche Leute mögen es stilistisch vorziehen, explizit darauf hinzuweisen, dass
Bar::f
es virtuell ist, wie folgt:Für mich ist es irgendwie überflüssig, in diesem Zusammenhang sowohl
virtual
und als auchfinal
Bezeichner für dieselbe Funktion zu verwenden (ebenfallsvirtual
undoverride
), aber in diesem Fall ist es eine Frage des Stils. Einige Leute finden möglicherweise, dassvirtual
hier etwas Wertvolles kommuniziert, ähnlich wie bei der Verwendungextern
für Funktionsdeklarationen mit externer Verknüpfung (obwohl es optional keine anderen Verknüpfungsqualifizierer gibt).quelle
private
Methode in IhrerVehicle
Klasse überschreiben ? Meinten Sie nichtprotected
stattdessen?private
sich nicht auf das Überschreiben (etwas kontraintuitiv). Öffentliche / geschützte / private Bezeichner für virtuelle Funktionen gelten nur für Anrufer und nicht für Overrider. Eine abgeleitete Klasse kann virtuelle Funktionen aus ihrer Basisklasse unabhängig von ihrer Sichtbarkeit überschreiben.protected
könnte etwas intuitiver sein. Ich greife einfach lieber nach der geringstmöglichen Sichtbarkeit, wann immer ich kann. Ich denke, der Grund, warum die Sprache so gestaltet ist, ist, dass ansonsten private virtuelle Mitgliedsfunktionen außerhalb des Kontextes der Freundschaft überhaupt keinen Sinn ergeben würden, da keine Klasse außer einem Freund sie dann überschreiben könnte, wenn Zugriffsspezifizierer im Kontext von Bedeutung wären überschreiben und nicht nur anrufen.protected
, um andere nicht zu verwirren. Am Ende habe ich zumindest einen Kommentar abgegeben, der beschreibt, wie private virtuelle Funktionen immer noch überschrieben werden können.Es ermöglicht viele Optimierungen, da zur Kompilierungszeit möglicherweise bekannt ist, welche Funktion aufgerufen wird.
Werfen Sie das Wort "Codegeruch" vorsichtig herum. "final" macht es nicht unmöglich, die Klasse zu erweitern. Doppelklicken Sie auf das "letzte" Wort, drücken Sie die Rücktaste und erweitern Sie die Klasse. JEDOCH final ist eine hervorragende Dokumentation, von der der Entwickler nicht erwartet, dass Sie die Funktion überschreiben, und aus diesem Grund sollte der nächste Entwickler sehr vorsichtig sein, da die Klasse möglicherweise nicht mehr richtig funktioniert, wenn die endgültige Methode auf diese Weise überschrieben wird.
quelle
final
und wird esvirtual
gleichzeitig verwendet?final
und seinoverride
.final
kann ihnen wirklich helfen