Die Frage im Thema deutet auf eine ziemlich häufige Verwirrung hin. Die Verwirrung ist häufig genug, dass C ++ FAQ lange Zeit gegen die Verwendung privater Virtuals plädierte, weil Verwirrung eine schlechte Sache zu sein schien.
Um die Verwirrung zuerst zu beseitigen: Ja, private virtuelle Funktionen können in den abgeleiteten Klassen überschrieben werden. Methoden abgeleiteter Klassen können keine virtuellen Funktionen aus der Basisklasse aufrufen, sie können jedoch ihre eigene Implementierung für sie bereitstellen. Laut Herb Sutter ermöglicht eine öffentliche nicht virtuelle Schnittstelle in der Basisklasse und eine private Implementierung, die in den abgeleiteten Klassen angepasst werden kann, eine bessere "Trennung der Schnittstellenspezifikation von der Spezifikation des anpassbaren Verhaltens der Implementierung". Mehr dazu lesen Sie in seinem Artikel "Virtualität" .
Der von Ihnen vorgestellte Code enthält jedoch noch eine weitere interessante Sache, die meiner Meinung nach etwas mehr Aufmerksamkeit verdient. Die öffentliche Schnittstelle besteht aus einer Reihe überladener nicht virtueller Funktionen, und diese Funktionen rufen nicht öffentliche, nicht überladene virtuelle Funktionen auf. Wie in der C ++ - Welt üblich, ist es eine Redewendung, es hat einen Namen und natürlich ist es nützlich. Der Name ist (Überraschung, Überraschung!)
"Öffentliche überladene Nicht-Virtuals rufen geschützte nicht überladene Virtuals auf"
Es hilft , die Ausblendungsregel richtig zu verwalten . Sie können mehr darüber lesen hier , aber ich werde versuchen , es kurz zu erklären.
Stellen Sie sich vor, dass virtuelle Funktionen der Engine
Klasse auch ihre Schnittstelle sind und es sich um eine Reihe überladener Funktionen handelt, die nicht rein virtuell sind. Wenn sie rein virtuell wären, könnte man immer noch auf dasselbe Problem stoßen, wie unten beschrieben, jedoch niedriger in der Klassenhierarchie.
class Engine
{
public:
virtual void SetState( int var, bool val ) {/*some implementation*/}
virtual void SetState( int var, int val ) {/*some implementation*/}
};
Nehmen wir nun an, Sie möchten eine abgeleitete Klasse erstellen und müssen nur für die Methode eine neue Implementierung bereitstellen, die zwei Ints als Argumente verwendet.
class MyTurbochargedV8 : public Engine
{
public:
// To prevent SetState( int var, bool val ) from the base class,
// from being hidden by the new implementation of the other overload (below),
// you have to put using declaration in the derived class
using Engine::SetState;
void SetState( int var, int val ) {/*new implementation*/}
};
Wenn Sie vergessen haben, die using-Deklaration in die abgeleitete Klasse einzufügen (oder die zweite Überladung neu zu definieren), können im folgenden Szenario Probleme auftreten.
MyTurbochargedV8* myV8 = new MyTurbochargedV8();
myV8->SetState(5, true);
Wenn Sie das Verstecken der Engine
Mitglieder nicht verhindert haben, lautet die Aussage:
myV8->SetState(5, true);
nennen würde void SetState( int var, int val )
von der abgeleiteten Klasse, die Umwandlung true
zu int
.
Wenn die Schnittstelle nicht virtuell ist und die virtuelle Implementierung nicht öffentlich ist, wie in Ihrem Beispiel, hat der Autor der abgeleiteten Klasse ein Problem weniger zu denken und kann einfach schreiben
class MyTurbochargedV8 : public Engine
{
private:
void SetStateInt(int var, int val ) {/*new implementation*/}
};
Private reine virtuelle Funktion ist die Basis der nicht-virtuellen Schnittstellensprache (OK, es ist nicht absolut immer rein virtuell, aber immer noch virtuell). Natürlich wird dies auch für andere Dinge verwendet, aber ich finde dies am nützlichsten (: In zwei Worten: In einer öffentlichen Funktion könnten Sie einige allgemeine Dinge (wie Protokollierung, Statistik usw.) am Anfang und setzen Am Ende der Funktion und dann "in der Mitte", um diese private virtuelle Funktion aufzurufen, ist dies für die jeweilige abgeleitete Klasse unterschiedlich. So etwas wie:
Rein virtuell - verpflichtet nur die abgeleiteten Klassen, es zu implementieren.
EDIT : Mehr dazu: Wikipedia :: NVI-idiom
quelle
Zum einen würde dies einer abgeleiteten Klasse ermöglichen, eine Funktion zu implementieren, die die Basisklasse (die die Deklaration der reinen virtuellen Funktion enthält) aufrufen kann.
quelle
BEARBEITEN: Klare Aussagen über die Fähigkeit zum Überschreiben und die Fähigkeit zum Zugreifen / Aufrufen.
Diese privaten Funktionen können überschrieben werden. Das folgende erfundene Beispiel funktioniert beispielsweise ( BEARBEITEN: Abgeleitete Klassenmethode privat machen und Aufruf der abgeleiteten Klassenmethode ablegen
main()
, um die Absicht des verwendeten Entwurfsmusters besser zu demonstrieren. ):Private
virtual
Methoden in einer Basisklasse wie die in Ihrem Code werden normalerweise zum Implementieren des Entwurfsmusters für Vorlagenmethoden verwendet . Dieses Entwurfsmuster ermöglicht es, das Verhalten eines Algorithmus in der Basisklasse zu ändern, ohne den Code in der Basisklasse zu ändern. Der obige Code, in dem die Basisklassenmethoden über einen Basisklassenzeiger aufgerufen werden, ist ein einfaches Beispiel für das Muster der Vorlagenmethode.quelle
Engine
undDerivedEngine
hat nichts mit dem zu tun , wasDerivedEngine
kann oder nicht außer Kraft setzen (oder Access, für diese Angelegenheit).Die private virtuelle Methode wird verwendet, um die Anzahl der abgeleiteten Klassen zu begrenzen, die die angegebene Funktion überschreiben können. Die abgeleiteten Klassen, die die private virtuelle Methode überschreiben müssen, müssen mit der Basisklasse befreundet sein.
Eine kurze Erklärung finden Sie auf DevX.com .
BEARBEITEN Eine private virtuelle Methode wird effektiv im Muster der Vorlagenmethode verwendet . Die abgeleiteten Klassen können die private virtuelle Methode überschreiben, aber die abgeleiteten Klassen können die private virtuelle Methode der Basisklasse nicht aufrufen (in Ihrem Beispiel
SetStateBool
undSetStateInt
). Nur die Basisklasse kann ihre private virtuelle Methode effektiv aufrufen ( Nur wenn abgeleitete Klassen die Basisimplementierung einer virtuellen Funktion aufrufen müssen, machen Sie die virtuelle Funktion geschützt ).Ein interessanter Artikel über Virtualität ist zu finden .
quelle
friend
der Basisklasse implementieren kann, muss sie zur Basisklasse gehören. Qt hat den gleichen Ansatz gewählt, als sie ihr XML-DOM-Dokumentmodell implementiert haben.TL; DR Antwort:
Sie können es wie eine andere Kapselungsebene behandeln - irgendwo zwischen geschützt und privat : Sie können es nicht aus der untergeordneten Klasse aufrufen, aber Sie können es überschreiben.
Dies ist nützlich, wenn Sie das Entwurfsmuster der Vorlagenmethode implementieren . Sie könnten geschützt verwenden , aber privat zusammen mit virtuell kann aufgrund der besseren Kapselung als bessere Wahl angesehen werden.
quelle