wake-up-neo.com

Was ist der Sinn einer privaten rein virtuellen Funktion?

Ich bin auf folgenden Code in einer Header-Datei gestoßen:

class Engine
{
public:
    void SetState( int var, bool val );
    {   SetStateBool( int var, bool val ); }

    void SetState( int var, int val );
    {   SetStateInt( int var, int val ); }
private:
    virtual void SetStateBool(int var, bool val ) = 0;    
    virtual void SetStateInt(int var, int val ) = 0;    
};

Für mich bedeutet dies, dass entweder die Klasse Engine oder eine davon abgeleitete Klasse die Implementierung für diese rein virtuellen Funktionen bereitstellen muss. Aber ich hätte nicht gedacht, dass abgeleitete Klassen Zugriff auf diese privaten Funktionen haben könnten, um sie erneut zu implementieren - warum sollten sie dann virtuell gemacht werden?

131
BeeBand

Die Frage im Thema lässt auf eine ziemlich häufige Verwirrung schließen. Die Verwirrung ist weit verbreitet, dass C++ FAQ sich lange Zeit gegen die Verwendung privater Virtuals ausgesprochen hat, weil Verwirrung eine schlechte Sache zu sein schien.

Um die Verwirrung 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 eine 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 Spezifikation der Schnittstelle von der Spezifikation des anpassbaren Verhaltens der Implementierung". Sie können mehr darüber in seinem Artikel lesen "Virtualität" .

In dem von Ihnen vorgestellten Code gibt es 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, hat einen Namen und ist natürlich 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 die virtuellen Funktionen der Klasse Engine 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 in der Klassenhierarchie niedriger.

class Engine
{
public:
    virtual void SetState( int var, bool val ) {/*some implementation*/}
    virtual void SetState( int var, int val )  {/*some implementation*/}
};

Angenommen, Sie möchten eine abgeleitete Klasse erstellen und müssen nur für die Methode eine neue Implementierung bereitstellen, für die zwei Ints als Argumente verwendet werden.

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 Sie im folgenden Szenario Probleme bekommen.

MyTurbochargedV8* myV8 = new MyTurbochargedV8();
myV8->SetState(5, true);

Wenn Sie das Verstecken der Engine -Mitglieder nicht verhindert haben, lautet die Anweisung:

myV8->SetState(5, true);

würde void SetState( int var, int val ) aus der abgeleiteten Klasse aufrufen und true in int konvertieren.

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 überlegen und kann einfach schreiben

class MyTurbochargedV8 : public Engine
{
private:
    void SetStateInt(int var, int val )  {/*new implementation*/}
};
201
Maciej Hehl

Private pure virtual Funktion ist die Basis der Nicht-virtuellen Schnittstelle Sprache (OK, es ist nicht unbedingt immer pure virtuell, aber dort 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, Statistiken usw.) an den Anfang und an den Anfang stellen am Ende der Funktion und dann "in der Mitte" zum Aufrufen dieser privaten virtuellen Funktion, die für die bestimmte abgeleitete Klasse unterschiedlich sein wird.

class Base
{
    // ..
public:
    void f();
private:
    virtual void DerivedClassSpecific() = 0;
   // ..
};
void Base::f()
{
    //.. Do some common stuff
    DerivedClassSpecific();
    //.. Some other common stuff
}
// ..

class Derived: public Base
{
    // ..
private:
    virtual void DerivedClassSpecific();
    //..
};
void Derived::DerivedClassSpecific()
{
    // ..
}

Reine virtuelle - nur die abgeleiteten Klassen verpflichtet, es zu implementieren.

[~ # ~] edit [~ # ~] : Mehr dazu: Wikipedia :: NVI-idiom

42
Kiril Kirov

Zum einen würde dies einer abgeleiteten Klasse ermöglichen, eine Funktion zu implementieren, die die Basisklasse (die die reine virtuelle Funktionsdeklaration enthält) aufrufen kann.

17

BEARBEITEN: Klargestellte Aussagen über die Fähigkeit zum Überschreiben und die Fähigkeit zum Zugriff/Aufrufen.

Diese privaten Funktionen können überschrieben werden. Das folgende konstruierte Beispiel funktioniert beispielsweise ( EDIT: Die abgeleitete Klassenmethode wird privat gemacht, und der Aufruf der abgeleiteten Klassenmethode wird in main() abgelegt, um die Absicht des Entwurfsmusters in besser zu demonstrieren benutze. ):

#include <iostream>

class Engine
{
public:
  void SetState( int var, bool val )
  {
    SetStateBool( var, val );
  }

  void SetState( int var, int val )
  {
    SetStateInt( var, val );
  }

private:

    virtual void SetStateBool(int var, bool val ) = 0;
    virtual void SetStateInt(int var, int val ) = 0;

};

class DerivedEngine : public Engine
{
private:
  virtual void SetStateBool(int var, bool val )
  {
    std::cout << "DerivedEngine::SetStateBool() called" << std::endl;
  }

  virtual void SetStateInt(int var, int val )
  {
    std::cout << "DerivedEngine::SetStateInt() called" << std::endl;
  }
};


int main()
{
  DerivedEngine e;
  Engine * be = &e;

  be->SetState(4, true);
  be->SetState(2, 1000);
}

Privatevirtual Methoden in einer Basisklasse wie die in Ihrem Code werden normalerweise verwendet, um das Entwurfsmuster Template Method zu implementieren. Mit diesem Entwurfsmuster kann das Verhalten eines Algorithmus in der Basisklasse geändert werden, 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.

4
Void

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 ein Freund der Basisklasse sein.

Eine kurze Erklärung finden Sie unter DevX.com .


[~ # ~] edit [~ # ~] Eine private virtuelle Methode wird effektiv in Template Method Pattern 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 und SetStateInt). Nur die Basisklasse kann ihre private virtuelle Methode effektiv aufrufen. ( Nur wenn abgeleitete Klassen die Basisimplementierung einer virtuellen Funktion aufrufen müssen, muss die virtuelle Funktion geschützt werden. ).

Ein interessanter Artikel ist zu finden über Virtuality .

3
Buhake Sindi

TL; DR Antwort:

Sie können es wie eine andere Ebene der Kapselung behandeln - irgendwo zwischen protected und private: Sie können es nicht aus der untergeordneten Klasse aufrufen, aber Sie können es überschreiben.

Dies ist hilfreich bei der Implementierung von Template-Methode Design Pattern. Sie könnten protected verwenden, aber private zusammen mit virtual können aufgrund der besseren Kapselung als bessere Wahl angesehen werden.

0
jaskmar