Wurde in GotW # 31 angesprochen. Zusammenfassung:
Dafür gibt es drei Hauptgründe mach das. # 1 ist alltäglich, # 2 ist ziemlich selten, und # 3 ist ein Workaround gelegentlich von fortgeschrittenen verwendet Programmierer arbeiten mit schwächeren Compiler.
Die meisten Programmierer sollten nur # 1 benutzen.
... Was für reine virtuelle Destruktoren ist.
In Effektives C ++ gibt Scott Meyers das Beispiel, dass es nützlich ist, wenn Sie Code durch Vererbung wiederverwenden. Er beginnt damit:
%Vor%Jetzt werden ModelA und ModelB auf die gleiche Weise geflogen, und das wird als eine übliche Methode angesehen, ein Flugzeug zu fliegen, also ist der Code in der Basisklasse. Allerdings werden nicht alle -Ebenen geflogen, und wir denken, dass Ebenen polymorph sind, also ist es virtuell.
Jetzt fügen wir ModelC hinzu, das anders geflogen sein muss, aber wir machen einen Fehler:
%Vor%Hoppla! ModelC wird abstürzen. Meyers würde es vorziehen, wenn der Compiler uns vor unserem Fehler warnt.
Also macht er fly
rein virtuell in Airplane mit einer Implementierung und setzt dann in ModelA und ModelB:
Nun, wenn wir nicht ausdrücklich in unserer abgeleiteten Klasse angeben, dass wir das Standardflugverhalten wollen, bekommen wir es nicht. Also, anstatt nur die Dokumentation, die uns alle Dinge sagt, die wir über unser neues Flugzeugmodell überprüfen müssen, sagt uns auch der Compiler.
Das macht den Job, aber ich denke, es ist ein bisschen schwach. Idealerweise haben wir stattdessen einen BoringlyFlyable-Mix, der die Standardimplementierung von fly enthält, und Code auf diese Weise wiederverwenden, anstatt Code in eine Basisklasse zu setzen, die bestimmte Dinge über Flugzeuge voraussetzt, die keine Anforderungen an Flugzeuge sind. Aber das erfordert CRTP, wenn die Funktion fly
tatsächlich etwas Bedeutendes tut:
Wenn PlaneA die Vererbung von BoringlyFlyable deklariert, aktiviert es über die Schnittstelle, dass es gültig ist, es auf die Standard-Art zu fliegen. Beachten Sie, dass BoringlyFlyable reine virtuelle Funktionen definieren könnte: vielleicht wäre getWings
eine gute Abstraktion. Aber da es eine Vorlage ist, muss es nicht.
Ich habe das Gefühl, dass dieses Muster alle Fälle ersetzen kann, in denen Sie eine reine virtuelle Funktion mit einer Implementierung versehen hätten - die Implementierung kann stattdessen in ein Mixing gehen, welche Klassen erben können, wenn sie es wollen. Aber ich kann nicht sofort beweisen, dass (zum Beispiel, wenn Airplane::fly
private Mitglieder verwendet, dann erfordert es eine erhebliche Neugestaltung, um es auf diese Weise zu tun), und CRTP ist wohl für den Anfänger sowieso ein wenig leistungsfähig. Außerdem ist es etwas mehr Code, der nicht wirklich Funktionalität oder Typsicherheit hinzufügt, es macht nur deutlich, was bereits in Meyers Entwurf implizit ist, dass einige Dinge nur durch Flattern ihrer Flügel fliegen können, während andere stattdessen andere Dinge tun müssen. Also ist meine Version keinesfalls ein totales Shoo-In.
Es gibt keinen Konflikt mit den beiden Konzepten, obwohl sie selten zusammen benutzt werden (wie OO-Puristen es nicht versöhnen können, aber das geht über den Rahmen dieser Frage / Antwort hinaus).
Die Idee ist, dass der rein virtuellen Funktion eine Implementierung gegeben wird, während gleichzeitig Unterklassen gezwungen werden, diese Implementierung zu überschreiben. Die Unterklassen können die Basisklassenfunktion aufrufen, um ein Standardverhalten bereitzustellen. Die Basis kann nicht instanziiert werden (sie ist "abstrakt"), weil die virtuelle (n) Funktion (en) rein ist, obwohl sie möglicherweise eine Implementierung hat.
Wikipedia fasst das gut zusammen:
Obwohl rein virtuelle Methoden in der Regel keine Implementierung in die Klasse, die sie für rein erklärt Virtuelle Methoden in C ++ sind erlaubt eine Implementierung in ihrem enthalten Klasse deklarieren, Fallback oder bereitstellen Standardverhalten einer abgeleiteten Klasse kann bei Bedarf delegieren.
Normalerweise müssen Sie keine Basisklassenimplementierungen für reine virtuelle Systeme bereitstellen. Aber es gibt eine Ausnahme: reine virtuelle Destruktoren . Wenn Ihre Basisklasse einen reinen virtuellen Destruktor hat, muss sie eine Implementierung haben . Warum brauchen Sie einen reinen virtuellen Destruktor anstatt nur einen virtuellen? In der Regel, um eine Basisklasse abstrakt zu machen, ohne dass eine andere Methode implementiert werden muss. Beispielsweise können Sie in einer Klasse, in der Sie die Standardimplementierung sinnvollerweise für jede Methode verwenden können, aber nicht möchten, dass Benutzer die Basisklasse instanziieren, nur den Destruktor als rein virtuell markieren.
BEARBEITEN:
Hier ist ein Code, der einige Möglichkeiten zum Aufrufen der Basisimplementierung veranschaulicht:
%Vor%Tags und Links c++