2016-04-27 5 views
7

C++ 11 hinzugefügt endgültig.Bedeutet C++ final in allen Aspekten endgültig?

Endlich!

Ich verstehe final macht zwei Dinge:

  • Macht eine Klasse nicht vererbbar.
  • Macht (virtuelle) Funktionen in einer Klasse nicht überschreibbar (in einer abgeleiteten Klasse).

Beide scheinen voneinander unabhängig zu sein. Aber zum Beispiel nehmen die folgenden:

class Foo 
{ 
    public: 
    virtual void bar() 
    { 
     //do something unimportant. 
    } 
}; 
class Baz final : public Foo 
{ 
    public: 
    void bar() /*final*/ override 
    { 
     //do something more important than Foo's bar. 
    } 
}; 

Von oben, glaube ich Bazfinal zu sein, ich sollte NICHT müssen angeben, dass seine virtual Memberfunktion bar auch final ist. Da Baz nicht vererbt werden kann, geht die Frage des Überschreibens bar nicht in den Geltungsbereich. Allerdings ist mein Compiler VC++ 2015 sehr leise darüber. Das habe ich im Moment noch nicht getestet.

Ich würde mich freuen, wenn jemand etwas Licht zu diesem Thema geben könnte. Ein Zitat aus dem Standard (falls vorhanden) würde sehr geschätzt werden. Bitte geben Sie auch Eckfälle an, die mir nicht bekannt sind und die meinen logischen Glauben zum Scheitern bringen könnten.

Also, meine Frage ist: einefinal classimplizit implizieren seinevirtualFunktionen Doesfinalals auch sein? Sollte es? Bitte klären Sie.


Der Grund Ich frage dies, weil final Funktionen qualifiziert werden für de-Virtualisierung, die eine große Optimierung ist. Jede Hilfe wird geschätzt.

+2

finale Funktionen werden nur dann de-virtualisiert, wenn der statische Typ mit dem übereinstimmt, in dem die Funktion final ist. Happenes selten, und ist oft ein Hinweis darauf, dass überhaupt kein virtuelles benötigt wird. – SergeyA

+0

@SergeyA Ja. Deshalb habe ich qualifiziert geschrieben. –

+2

Wenn der Compiler garantieren kann, dass er keinen virtuellen Aufruf benötigt, wird er keinen virtuellen Aufruf einfügen (als Optimierung). Der Compiler ist clever und hat dies bereits vor der Einführung von final getan. Aber ich nehme an, es ist jetzt einfacher. –

Antwort

8

Der Grund, warum ich das frage, ist, weil die endgültigen Funktionen für die De-Virtualisierung qualifiziert werden, was eine großartige Optimierung ist.

Müssen sie? "De-Virtualisierung" ist nicht Teil des C++ - Standards. Oder zumindest nicht wirklich.

Die De-Virtualisierung ist lediglich eine Folge der "Als-ob" -Regel, die besagt, dass die Implementierung tun kann, was sie will, solange sich die Implementierung so verhält, als würde sie tun, was der Standard sagt.

Wenn der Compiler zur Kompilierungszeit erkennen kann, dass ein bestimmter Aufruf einer virtuellen Memberfunktion über einen polymorphen Typ unzweifelhaft eine bestimmte Version dieser Funktion aufruft, darf die Verwendung der virtuellen Dispatchlogik vermieden werden die Funktion statisch aufrufen. Das verhält sich "als ob" es die virtuelle Dispatch-Logik verwendet hätte, da der Compiler beweisen kann, dass dies die Funktion ist, die aufgerufen worden wäre.

Daher definiert der Standard nicht, wann eine De-Virtualisierung erlaubt/verboten ist. Ein Compiler kann beim Inlining einer Klasse, die einen Zeiger auf einen virtuellen Typ annimmt, feststellen, dass der übergebene Zeiger auf eine lokale Stapelvariable verweist, die in der Funktion deklariert ist, in der sie inline ist. Oder dass der Compiler ein bestimmtes Inline-/Aufrufdiagramm zum Ausgangspunkt eines bestimmten polymorphen Zeigers/Verweises zurückverfolgen kann. In diesen Fällen kann der Compiler Aufrufe in diesen Typ de-virtualisieren. Aber nur wenn es schlau genug ist.

Wird ein Compiler alle virtuellen Funktionsaufrufe in eine final Klasse devirtualisieren, unabhängig davon, ob diese Methoden selbst als final deklariert sind? Es kann. Es darf nicht. Es darf nicht einmal Aufrufe an Methoden dekompilieren, die auf dem polymorphen Typ final deklariert sind. Das ist eine gültige (wenn nicht besonders helle) Implementierung.

Die Frage, die Sie stellen, ist implementierungsspezifisch. Es kann von Compiler zu Compiler variieren.

Eine Klasse, die jedoch als final deklariert wird, sollte, wie Sie darauf hingewiesen haben, ausreichende Informationen für den Compiler zum Devirtualisieren aller Aufrufe von Zeigern/Referenzen auf die Klassenart final sein. Wenn ein Compiler dies nicht tut, ist das ein Qualitätsproblem, kein Standard.

+0

Nein. Der gesamte Virtualisierungsmechanismus (mit virtuellen Tabellen und Zeigern) ist nicht Teil des Standards. Ich hatte gehofft, dass die Implementierer Details über die implementierten Implementierungsdetails veröffentlichen würden. Wie auch immer, +1 für die klare Antwort. –

4

den Entwurf C++ Standard Zitat von hier [class.virtual/4]:

Wenn eine virtuelle Funktion f in irgendeiner Klasse B mit dem virt-Spezifiziererfinal und in einer Klasse markiert D von B eine Funktion abgeleitet D::f überschreibt B::f, das Programm ist schlecht gebildet.

Und hier [class/3]:

Wenn eine Klasse mit dem Klasse-virt-Spezifiziererfinal markiert und es erscheint als ein Basistyp-Spezifizierer in einer Basis-Klausel (Klausel [class.derived]), das Programm ist schlecht gebildet.

Also, in Antwort auf die Frage;

Hat ein finalclass implizit implizieren seine virtual Funktionen final als auch zu sein? Sollte es? Bitte klären Sie.

Also, zumindest nicht formal. Jeder Versuch, gegen eine der beiden Regeln zu verstoßen, führt in beiden Fällen zum selben Ergebnis. Das Programm ist schlecht formuliert und wird nicht kompiliert. A final class bedeutet, dass die Klasse nicht abgeleitet werden kann, daher können ihre virtual Methoden nicht überschrieben werden.

Sollte es? Zumindest formal, wahrscheinlich nicht; Sie sind verwandt, aber sie sind nicht dasselbe. Es besteht auch keine Notwendigkeit, dass die eine die andere impliziert, die Wirkung folgt natürlich. Alle Verstöße haben das gleiche Ergebnis, eine fehlgeschlagene Kompilierung (hoffentlich mit entsprechenden Fehlermeldungen zur Unterscheidung der beiden).


Um Ihre Motivation für die Abfrage und die De-Virtualisierung der virtuellen Anrufe zu berühren. Dies ist nicht immer sofort von der final der Klasse oder Methode betroffen (obwohl sie Hilfe anbieten), gelten die normalen Regeln der virtuellen Funktionen und Klassenhierarchie. Wenn der Compiler bestimmen kann, dass zur Laufzeit immer eine bestimmte Methode aufgerufen wird (z. B. mit einem automatischen Objekt, d.h. "auf dem Stapel"), könnte sie an optimisation anyway anwenden, unabhängig davon, ob die Methode endgültig ist oder nicht. Diese Optimierungen fallen unter die Als-ob-Regel, die dem Compiler apply any transformation erlauben, solange das beobachtbare Verhalten so ist, als wenn der ursprüngliche Code ausgeführt worden wäre.

+0

Uh? Wie ist es _ill-formed_? Die "letzte Klasse" soll ** ihre eigene praktisch ererbte Mitgliedsfunktion als "final" implizieren. Das obige Beispiel kompiliert in beiden Testfällen: Wenn der "finale" Spezifizierer an die virtuelle Funktion angehängt ist und wenn nicht (ohne einen Fehler/Warnung/Informationsmeldungen wie auch immer). Bitte erkläre. –

+0

Ja, die Standardformulierung, die Sie (wie gefordert) beziehen, bezieht sich auf den Versuch, eine endgültige Methode zu überschreiben oder eine letzte Klasse als Basis zu verwenden. Das ist der Zweck des Finales. Das abschließende Schlüsselwort kann kombiniert werden, es gibt kein Problem damit, die beiden Fälle bedeuten nicht dasselbe. Es könnte sogar empfohlen werden, wenn Sie Bedenken hinsichtlich der zukünftigen Wartung des Codes haben. Die Optimierungen, die Sie erwähnen, sind eine Qualität von Implementierungsproblemen und wurden bereits lange vor der Einführung des Standards in den Standard implementiert. – Niall