Was ist der Zweck des final
Schlüsselworts in C ++ 11 für Funktionen? Ich verstehe, dass es das Überschreiben von Funktionen durch abgeleitete Klassen verhindert, aber wenn dies der Fall ist, reicht es dann nicht aus, Ihre final
Funktionen als nicht virtuell zu deklarieren ? Fehlt mir hier noch etwas?
142
virtual
Schlüsselwort verwenden oder nicht.func
ist es nicht virtuell, es gibt also nichts zu überschreiben und somit nichts alsoverride
oder zu markierenfinal
.Antworten:
Was Sie vermissen, wie idljarn bereits in einem Kommentar erwähnt hat, ist, dass Sie eine Funktion aus einer Basisklasse möglicherweise nicht als nicht virtuell markieren können , wenn Sie sie überschreiben :
quelle
virtual
kann Fehler verursachen, und C ++ 11 fügte dasoverride
Tag einer Funktion hinzu, die diese Situation erkennt und nicht kompiliert werden kann, wenn sich eine Funktion, die überschreiben soll, tatsächlich verbirgtEs soll verhindern, dass eine Klasse vererbt wird. Aus Wikipedia :
Es wird auch verwendet, um eine virtuelle Funktion zu markieren, um zu verhindern, dass sie in den abgeleiteten Klassen überschrieben wird:
Wikipedia macht weiter einen interessanten Punkt :
Das heißt, Folgendes ist zulässig:
quelle
"final" ermöglicht es einer Compileroptimierung auch, den indirekten Aufruf zu umgehen:
Mit "final" kann der Compiler
CDerived::DoSomething()
direkt von innenBlah()
oder sogar inline aufrufen . Ohne sie muss ein indirekter Aufruf innerhalb von generiert werden,Blah()
daBlah()
er innerhalb einer abgeleiteten Klasse aufgerufen werden kann, die überschrieben wurdeDoSomething()
.quelle
Den semantischen Aspekten von "final" gibt es nichts hinzuzufügen.
Aber ich möchte zu Chris Green's Kommentar hinzufügen, dass "final" in nicht allzu ferner Zukunft eine sehr wichtige Compiler-Optimierungstechnik werden könnte. Nicht nur in dem von ihm erwähnten einfachen Fall, sondern auch für komplexere reale Klassenhierarchien, die durch "final" "geschlossen" werden können, wodurch Compiler einen effizienteren Dispatching-Code generieren können als mit dem üblichen vtable-Ansatz.
Ein wesentlicher Nachteil von vtables besteht darin, dass für ein solches virtuelles Objekt (unter der Annahme von 64 Bit auf einer typischen Intel-CPU) der Zeiger allein 25% (8 von 64 Byte) einer Cache-Zeile verbraucht. Bei den Bewerbungen, die ich gerne schreibe, tut das sehr weh. (Und meiner Erfahrung nach ist es aus puristischer Sicht das wichtigste Argument gegen C ++, dh von C-Programmierern.)
In Anwendungen, die extreme Leistung erfordern, was für C ++ nicht so ungewöhnlich ist, kann dies in der Tat fantastisch werden, da dieses Problem nicht manuell im C-Stil oder durch seltsames Vorlagen-Jonglieren umgangen werden muss.
Diese Technik ist als Devirtualisierung bekannt . Ein Begriff, an den man sich erinnern sollte. :-)
Es gibt eine großartige Rede von Andrei Alexandrescu, die ziemlich gut erklärt, wie Sie solche Situationen heute umgehen können und wie "final" dazu beitragen könnte, ähnliche Fälle "automatisch" in Zukunft zu lösen (mit den Zuhörern besprochen):
http://channel9.msdn.com/Events/GoingNative/2013/Writing-Quick-Code-in-Cpp-Quickly
quelle
Final kann nicht auf nicht virtuelle Funktionen angewendet werden.
Es wäre nicht sehr sinnvoll, eine nicht virtuelle Methode als "endgültig" markieren zu können. Gegeben
a->foo()
wird immer anrufenA::foo
.Aber wenn A
virtual
:: foo wäre, würde B :: foo es überschreiben. Dies könnte unerwünscht sein, und daher wäre es sinnvoll, die virtuelle Funktion endgültig zu machen.Die Frage ist jedoch, warum endgültige virtuelle Funktionen zulässig sind. Wenn Sie eine tiefe Hierarchie haben:
Dann
final
legt das einen "Boden" fest, wie viel Überschreiben getan werden kann. Andere Klassen können A und B erweitern und ihre überschreibenfoo
, aber wenn eine Klasse C erweitert, ist dies nicht zulässig.Es macht also wahrscheinlich keinen Sinn, das "Top-Level" -Foo zu machen
final
, aber es könnte weiter unten Sinn machen.(Ich denke jedoch, dass es Raum gibt, die Wörter final zu erweitern und auf nicht virtuelle Mitglieder zu überschreiben. Sie hätten jedoch eine andere Bedeutung.)
quelle
final
. Wenn Sie beispielsweise wissen, dass Sie alleShape
s möchten - etwasfoo()
Vordefiniertes und Bestimmtes, das keine abgeleitete Form ändern sollte. Oder irre ich mich und es gibt ein besseres Muster für diesen Fall? EDIT: Oh, vielleicht, weil man in diesem Fall einfach nicht die oberste Ebene erreichenfoo()
virtual
sollte? Trotzdem kann es ausgeblendet werden, auch wenn es korrekt (polymorph) überShape*
...Ein Anwendungsfall für das Schlüsselwort 'final', das mir gefällt, lautet wie folgt:
quelle
final
Fügt eine explizite Absicht hinzu, Ihre Funktion nicht überschreiben zu lassen, und verursacht einen Compilerfehler, falls dies verletzt wird:In der jetzigen Form wird der Code kompiliert und
B::foo
überschriebenA::foo
.B::foo
ist übrigens auch virtuell. Wenn wir jedoch # 1 in ändernvirtual int foo() final
, ist dies ein Compilerfehler, und wir dürfenA::foo
in abgeleiteten Klassen nicht weiter überschreiben .Beachten Sie, dass wir dadurch eine neue Hierarchie nicht "wieder öffnen" können, dh es gibt keine Möglichkeit,
B::foo
eine neue, nicht verwandte Funktion zu erstellen, die unabhängig an der Spitze einer neuen virtuellen Hierarchie stehen kann. Sobald eine Funktion endgültig ist, kann sie in keiner abgeleiteten Klasse mehr deklariert werden.quelle
Mit dem letzten Schlüsselwort können Sie eine virtuelle Methode deklarieren, N-mal überschreiben und dann festlegen, dass dies nicht mehr überschrieben werden kann. Es wäre nützlich, um die Verwendung Ihrer abgeleiteten Klasse einzuschränken, damit Sie sagen können: "Ich weiß, dass Sie mit meiner Superklasse dies überschreiben können, aber wenn Sie von mir ableiten möchten, können Sie das nicht!".
Wie andere Poster betonten, kann es nicht auf nicht virtuelle Funktionen angewendet werden.
Ein Zweck des endgültigen Schlüsselworts besteht darin, ein versehentliches Überschreiben einer Methode zu verhindern. In meinem Beispiel war DoStuff () möglicherweise eine Hilfsfunktion, die die abgeleitete Klasse einfach umbenennen muss, um ein korrektes Verhalten zu erzielen. Ohne final würde der Fehler erst beim Testen entdeckt.
quelle
Das letzte Schlüsselwort in C ++ verhindert beim Hinzufügen zu einer Funktion, dass diese von einer Basisklasse überschrieben wird. Auch beim Hinzufügen zu einer Klasse wird die Vererbung jeglicher Art verhindert. Betrachten Sie das folgende Beispiel, das die Verwendung des endgültigen Spezifizierers zeigt. Dieses Programm schlägt bei der Kompilierung fehl.
Ebenfalls:
quelle
Ergänzung zu Mario Knezovićs Antwort:
Der obige Code zeigt die Theorie, wurde jedoch nicht auf echten Compilern getestet. Sehr geschätzt, wenn jemand eine zerlegte Ausgabe einfügt.
quelle