wake-up-neo.com

Virtuell / rein virtuell erklärt

Was genau bedeutet es, wenn eine Funktion als virtuell definiert ist und dasselbe wie rein virtuell?

320
Justin

Von Wikipedia's Virtual function ...

Bei der objektorientierten Programmierung ist in Sprachen wie C++ und Object Pascal eine virtuelle Funktion oder virtuelle Methode eine vererbbare und überschreibbare Funktion oder Methode, für die der dynamische Versand erleichtert wird. Dieses Konzept ist ein wichtiger Bestandteil des (Laufzeit-) Polymorphismus-Teils der objektorientierten Programmierung (OOP). Kurz gesagt, eine virtuelle Funktion definiert eine auszuführende Zielfunktion, aber das Ziel ist möglicherweise zur Kompilierungszeit nicht bekannt.

Im Gegensatz zu einer nicht virtuellen Funktion wird beim Überschreiben einer virtuellen Funktion die am häufigsten abgeleitete Version auf allen Ebenen der Klassenhierarchie verwendet und nicht nur auf der Ebene, auf der sie erstellt wurde. Wenn eine Methode der Basisklasse eine virtuelle Methode aufruft , wird daher die in der abgeleiteten Klasse definierte Version anstelle der in der Basisklasse definierten Version verwendet .

Dies steht im Gegensatz zu nicht virtuellen Funktionen, die in einer abgeleiteten Klasse immer noch überschrieben werden können. Die "neue" Version wird jedoch nur von der abgeleiteten Klasse und darunter verwendet, ändert jedoch die Funktionalität der Basisklasse überhaupt nicht.

wohingegen..

Eine rein virtuelle Funktion oder eine rein virtuelle Methode ist eine virtuelle Funktion, die von einer abgeleiteten Klasse implementiert werden muss, wenn die abgeleitete Klasse nicht abstrakt ist.

Wenn eine rein virtuelle Methode vorhanden ist, ist die Klasse "abstrakt" und kann nicht allein instanziiert werden. Stattdessen muss eine abgeleitete Klasse verwendet werden, die die rein virtuellen Methoden implementiert. Eine rein virtuelle Klasse ist in der Basisklasse überhaupt nicht definiert, daher muss sie von einer abgeleiteten Klasse definiert werden , oder diese abgeleitete Klasse ist ebenfalls abstrakt. und kann nicht instanziiert werden. Nur eine Klasse ohne abstrakte Methoden kann instanziiert werden.

Eine virtuelle Klasse bietet eine Möglichkeit, die Funktionalität der Basisklasse zu überschreiben, und eine rein virtuelle Klasse benötigt .

311
Diego Dias

Ich möchte die Definition von virtuell in Wikipedia kommentieren, die hier von mehreren wiederholt wird. [Zu der Zeit, als diese Antwort geschrieben wurde,] definierte Wikipedia eine virtuelle Methode als eine Methode, die in Unterklassen überschrieben werden kann. [Glücklicherweise wurde Wikipedia seitdem bearbeitet und erklärt dies nun korrekt.] Das ist falsch: Jede Methode, nicht nur virtuelle, kann in Unterklassen überschrieben werden. Was virtuell macht, ist, Ihnen Polymorphismus zu geben, das heißt, die Fähigkeit, zur Laufzeit die am häufigsten abgeleitete Außerkraftsetzung einer Methode auszuwählen.

Betrachten Sie den folgenden Code:

#include <iostream>
using namespace std;

class Base {
public:
    void NonVirtual() {
        cout << "Base NonVirtual called.\n";
    }
    virtual void Virtual() {
        cout << "Base Virtual called.\n";
    }
};
class Derived : public Base {
public:
    void NonVirtual() {
        cout << "Derived NonVirtual called.\n";
    }
    void Virtual() {
        cout << "Derived Virtual called.\n";
    }
};

int main() {
    Base* bBase = new Base();
    Base* bDerived = new Derived();

    bBase->NonVirtual();
    bBase->Virtual();
    bDerived->NonVirtual();
    bDerived->Virtual();
}

Was ist die Ausgabe dieses Programms?

Base NonVirtual called.
Base Virtual called.
Base NonVirtual called.
Derived Virtual called.

Abgeleitet überschreibt jede Methode von Base: nicht nur die virtuelle, sondern auch die nicht virtuelle.

Wenn Sie einen Base-Pointer-to-Derived (bDerived) haben, wird beim Aufrufen von NonVirtual die Implementierung der Base-Klasse aufgerufen. Dies wird zur Kompilierungszeit behoben: Der Compiler erkennt, dass bDerived eine Base * ist, dass NonVirtual nicht virtuell ist, und führt die Auflösung für die Klasse Base durch.

Wenn Sie jedoch Virtual aufrufen, wird die Implementierung der abgeleiteten Klasse aufgerufen. Aufgrund des Schlüsselworts virtual erfolgt die Auswahl der Methode zur Laufzeit, nicht zur Kompilierungszeit. Was hier zur Kompilierungszeit passiert, ist, dass der Compiler sieht, dass dies eine Base * ist und dass er eine virtuelle Methode aufruft, sodass er einen Aufruf an die vtable anstelle der Klasse Base einfügt. Diese vtable wird zur Laufzeit instanziiert, daher die Laufzeitauflösung für die am häufigsten abgeleitete Außerkraftsetzung.

Ich hoffe das war nicht zu verwirrend. Kurz gesagt, jede Methode kann überschrieben werden, aber nur mit virtuellen Methoden erhalten Sie einen Polymorphismus, dh eine Laufzeitauswahl der am häufigsten abgeleiteten Überschreibung. In der Praxis wird das Überschreiben einer nicht virtuellen Methode jedoch als schlechte Praxis angesehen und nur selten verwendet. Daher denken viele Leute (einschließlich derjenigen, die diesen Wikipedia-Artikel geschrieben haben), dass nur virtuelle Methoden überschrieben werden können.

197
Asik

Das virtuelle Schlüsselwort gibt C++ die Fähigkeit, Polymorphismus zu unterstützen. Wenn Sie einen Zeiger auf ein Objekt einer Klasse haben, z.

class Animal
{
  public:
    virtual int GetNumberOfLegs() = 0;
};

class Duck : public Animal
{
  public:
     int GetNumberOfLegs() { return 2; }
};

class Horse : public Animal
{
  public:
     int GetNumberOfLegs() { return 4; }
};

void SomeFunction(Animal * pAnimal)
{
  cout << pAnimal->GetNumberOfLegs();
}

In diesem (albernen) Beispiel gibt die GetNumberOfLegs () -Funktion die entsprechende Zahl basierend auf der Klasse des Objekts zurück, für das sie aufgerufen wird.

Betrachten Sie nun die Funktion 'SomeFunction'. Es ist egal, welche Art von Tierobjekt ihm übergeben wird, solange es von Animal abgeleitet ist. Der Compiler wandelt automatisch jede von Animal abgeleitete Klasse in eine Animal-Klasse um, da es sich um eine Basisklasse handelt.

Wenn wir das machen:

Duck d;
SomeFunction(&d);

es würde '2' ausgeben. Wenn wir das machen:

Horse h;
SomeFunction(&h);

es würde '4' ausgeben. Das können wir nicht machen:

Animal a;
SomeFunction(&a);

weil es nicht kompiliert werden kann, weil die virtuelle Funktion GetNumberOfLegs () rein ist, was bedeutet, dass es durch Ableiten von Klassen (Unterklassen) implementiert werden muss.

Reine virtuelle Funktionen werden hauptsächlich verwendet, um Folgendes zu definieren:

a) abstrakte Klassen

Dies sind Basisklassen, von denen Sie ableiten und dann die rein virtuellen Funktionen implementieren müssen.

b) Schnittstellen

Dies sind "leere" Klassen, in denen alle Funktionen rein virtuell sind und Sie daher alle Funktionen ableiten und dann implementieren müssen.

112
JBRWilkinson

In einer C++ - Klasse ist virtual das Schlüsselwort, das angibt, dass eine Methode von einer Unterklasse überschrieben (d. H. Implementiert) werden kann. Zum Beispiel:

class Shape 
{
  public:
    Shape();
    virtual ~Shape();

    std::string getName() // not overridable
    {
      return m_name;
    }

    void setName( const std::string& name ) // not overridable
    {
      m_name = name;
    }

  protected:
    virtual void initShape() // overridable
    {
      setName("Generic Shape");
    }

  private:
    std::string m_name;
};

In diesem Fall kann eine Unterklasse die Funktion initShape außer Kraft setzen, um spezielle Aufgaben auszuführen:

class Square : public Shape
{
  public: 
    Square();
    virtual ~Square();

  protected:
    virtual void initShape() // override the Shape::initShape function
    {
      setName("Square");
    }
}

Der Begriff pure virtual bezieht sich auf virtuelle Funktionen, die von einer Unterklasse implementiert werden müssen und von der Basisklasse nicht implementiert wurden. Sie bezeichnen eine Methode als rein virtuell, indem Sie das Schlüsselwort virtual verwenden und am Ende der Methodendeklaration ein = 0 hinzufügen.

Wenn Sie Shape :: initShape also rein virtuell machen möchten, würden Sie Folgendes tun:

class Shape 
{
 ...
    virtual void initShape() = 0; // pure virtual method
 ... 
};

Indem Sie Ihrer Klasse eine rein virtuelle Methode hinzufügen, machen Sie die Klasse zu einer abstrakte Basisklasse , was sehr praktisch ist, um Schnittstellen von der Implementierung zu trennen.

30
Nick Haddad

"Virtuell" bedeutet, dass die Methode in Unterklassen überschrieben werden kann, jedoch eine direkt aufrufbare Implementierung in der Basisklasse aufweist. "Rein virtuell" bedeutet, dass es sich um eine virtuelle Methode ohne direkt aufrufbare Implementierung handelt. Eine solche Methode muss mindestens einmal in der Vererbungshierarchie überschrieben werden. Wenn eine Klasse über nicht implementierte virtuelle Methoden verfügt, können Objekte dieser Klasse nicht erstellt werden Kompilierung wird fehlschlagen.

@quark weist darauf hin, dass rein virtuelle Methoden eine Implementierung haben können , aber da rein virtuelle Methoden überschrieben werden müssen, kann die Standardimplementierung nicht direkt sein namens. Hier ist ein Beispiel für eine rein virtuelle Methode mit einem Standardwert:

#include <cstdio>

class A {
public:
    virtual void Hello() = 0;
};

void A::Hello() {
    printf("A::Hello\n");
}

class B : public A {
public:
    void Hello() {
        printf("B::Hello\n");
        A::Hello();
    }
};

int main() {
    /* Prints:
           B::Hello
           A::Hello
    */
    B b;
    b.Hello();
    return 0;
}

Laut Kommentaren ist es compilerspezifisch, ob die Kompilierung fehlschlägt oder nicht. Zumindest in GCC 4.3.3 wird Folgendes nicht kompiliert:

class A {
public:
    virtual void Hello() = 0;
};

int main()
{
    A a;
    return 0;
}

Ausgabe:

$ g++ -c virt.cpp 
virt.cpp: In function ‘int main()’:
virt.cpp:8: error: cannot declare variable ‘a’ to be of abstract type ‘A’
virt.cpp:1: note:   because the following virtual functions are pure within ‘A’:
virt.cpp:3: note:   virtual void A::Hello()
15
John Millikin

Wie funktioniert das virtuelle Schlüsselwort?

Angenommen, der Mensch ist eine Basisklasse, der Inder ist vom Menschen abgeleitet.

Class Man
{
 public: 
   virtual void do_work()
   {}
}

Class Indian : public Man
{
 public: 
   void do_work()
   {}
}

Wenn Sie do_work () als virtuell deklarieren, bedeutet dies einfach: Welches do_work () aufgerufen werden soll, wird NUR zur Laufzeit festgelegt.

Angenommen, ich tue,

Man *man;
man = new Indian();
man->do_work(); // Indian's do work is only called.

Wenn virtual nicht verwendet wird, wird derselbe vom Compiler statisch bestimmt oder statisch gebunden, je nachdem, welches Objekt aufruft. Wenn also ein Objekt von Man do_work () aufruft, heißt das von Man do_work (), AUCH WENN ES AUF EIN INDISCHES OBJEKT ZEIGT

Ich glaube, dass die Antwort mit den meisten Stimmen irreführend ist - Jede Methode, ob virtuell oder nicht, kann eine überschriebene Implementierung in der abgeleiteten Klasse haben. Unter spezieller Bezugnahme auf C++ ist der richtige Unterschied die Laufzeitbindung (wenn Virtual verwendet wird) und die Kompilierungszeit (wenn Virtual nicht verwendet wird, aber eine Methode überschrieben wird und ein Basiszeiger auf ein abgeleitetes Objekt zeigt) der verknüpften Funktionen.

Es scheint einen weiteren irreführenden Kommentar zu geben, der besagt:

"Justin, 'pure virtual' ist nur ein Begriff (kein Schlüsselwort, siehe meine Antwort unten), der bedeutet:" Diese Funktion kann von der Basisklasse nicht implementiert werden. "

DIESE IS FALSCH! Rein virtuelle Funktionen können auch einen Körper haben UND KÖNNEN DURCHGEFÜHRT WERDEN! Die Wahrheit ist, dass die reine virtuelle Funktion einer abstrakten Klasse statisch genannt werden kann! Zwei sehr gute Autoren sind Bjarne Stroustrup und Stan Lippman ... weil sie die Sprache geschrieben haben.

9
McMurdo

In Simula, C++ und C #, die standardmäßig eine statische Methodenbindung verwenden, kann der Programmierer angeben, dass bestimmte Methoden eine dynamische Bindung verwenden sollen, indem er sie als virtuell kennzeichnet. Die dynamische Methodenbindung spielt eine zentrale Rolle bei der objektorientierten Programmierung.

Objektorientierte Programmierung erfordert drei grundlegende Konzepte: Kapselung, Vererbung und dynamische Methodenbindung.

Encapsulation ermöglicht es, die Implementierungsdetails einer Abstraktion hinter einer einfachen Schnittstelle zu verbergen.

Vererbung ermöglicht das Definieren einer neuen Abstraktion als Erweiterung oder Verfeinerung einer vorhandenen Abstraktion, wobei einige oder alle ihrer Merkmale automatisch erhalten werden.

Dynamische Methodenbindung ermöglicht es der neuen Abstraktion, ihr neues Verhalten anzuzeigen, auch wenn sie in einem Kontext verwendet wird, der die alte Abstraktion erwartet.

2
PJT

Virtuelle Methoden KÖNNEN durch Ableiten von Klassen überschrieben werden, benötigen jedoch eine Implementierung in der Basisklasse (diejenige, die überschrieben wird).

Reine virtuelle Methoden haben keine Implementierung der Basisklasse. Sie müssen durch abgeleitete Klassen definiert werden. (Also technisch übersteuert ist nicht der richtige Begriff, weil es nichts zu übersteuern gibt).

Virtual entspricht dem Standardverhalten Java, wenn die abgeleitete Klasse eine Methode der Basisklasse überschreibt.

Reine virtuelle Methoden entsprechen dem Verhalten abstrakter Methoden innerhalb abstrakter Klassen. Und eine Klasse, die nur rein virtuelle Methoden und Konstanten enthält, wäre der cpp-Pendant zu einem Interface.

1
johannes_lalala

Eine virtuelle Funktion ist eine Mitgliedsfunktion, die in einer Basisklasse deklariert und von einer abgeleiteten Klasse neu definiert wird. Virtuelle Funktionen sind in der Reihenfolge ihrer Vererbung hierarchisch. Wenn eine abgeleitete Klasse eine virtuelle Funktion nicht überschreibt, wird die in ihrer Basisklasse definierte Funktion verwendet.

Eine rein virtuelle Funktion enthält keine Definition in Bezug auf die Basisklasse. In der Basisklasse ist keine Implementierung vorhanden. Jede abgeleitete Klasse muss diese Funktion überschreiben.

0
rashedcs

Reine virtuelle Funktion

versuchen Sie diesen Code

#include <iostream>
using namespace std;
class aClassWithPureVirtualFunction
{

public:

    virtual void sayHellow()=0;

};

class anotherClass:aClassWithPureVirtualFunction
{

public:

    void sayHellow()
    {

        cout<<"hellow World";
    }

};
int main()
{
    //aClassWithPureVirtualFunction virtualObject;
    /*
     This not possible to create object of a class that contain pure virtual function
    */
    anotherClass object;
    object.sayHellow();
}

In der Klasse anotherClass entfernen Sie die Funktion sayHellow und führen Sie den Code aus. Sie erhalten eine Fehlermeldung: Wenn eine Klasse eine reine virtuelle Funktion enthält, kann aus dieser Klasse kein Objekt erstellt werden und sie wird geerbt. Die abgeleitete Klasse muss diese Funktion implementieren.

Virtuelle Funktion

versuchen Sie es mit einem anderen Code

#include <iostream>
using namespace std;
class aClassWithPureVirtualFunction
{

public:

    virtual void sayHellow()
    {
        cout<<"from base\n";
    }

};

class anotherClass:public aClassWithPureVirtualFunction
{

public:

    void sayHellow()
    {

        cout<<"from derived \n";
    }

};
int main()
{
    aClassWithPureVirtualFunction *baseObject=new aClassWithPureVirtualFunction;
    baseObject->sayHellow();///call base one

    baseObject=new anotherClass;
    baseObject->sayHellow();////call the derived one!

}

Hier wird die Funktion sayHellow in der Basisklasse als virtuell markiert. Sie gibt den Compiler an, der versucht, die Funktion in der abgeleiteten Klasse zu suchen und die Funktion zu implementieren. Wird sie nicht gefunden, führen Sie die Basisklasse aus. Danke

"Eine virtuelle Funktion oder virtuelle Methode ist eine Funktion oder Methode, deren Verhalten innerhalb einer erbenden Klasse von einer Funktion mit derselben Signatur überschrieben werden kann" - wikipedia

Dies ist keine gute Erklärung für virtuelle Funktionen. Denn selbst wenn ein Member nicht virtuell ist, kann es durch das Erben von Klassen überschrieben werden. Sie können versuchen, es selbst zu sehen.

Der Unterschied zeigt sich, wenn eine Funktion eine Basisklasse als Parameter verwendet. Wenn Sie eine vererbende Klasse als Eingabe angeben, verwendet diese Funktion die Basisklassenimplementierung der Überschreibungsfunktion. Wenn diese Funktion jedoch virtuell ist, wird die in der Ableitungsklasse implementierte Funktion verwendet.

0
can
  • Virtuelle Funktionen müssen eine Definition in der Basisklasse und auch in der abgeleiteten Klasse haben, sind jedoch nicht erforderlich. Beispielsweise ist die Funktion ToString () oder toString () eine virtuelle Funktion, sodass Sie Ihre eigene Implementierung bereitstellen können, indem Sie sie in benutzerdefinierten Klassen überschreiben.

  • Virtuelle Funktionen werden in normalen Klassen deklariert und definiert.

  • Es muss eine reine virtuelle Funktion deklariert werden, die mit "= 0" endet und nur in einer abstrakten Klasse deklariert werden kann.

  • Eine abstrakte Klasse mit einer oder mehreren rein virtuellen Funktionen kann keine Definition (en) dieser rein virtuellen Funktionen haben. Dies bedeutet, dass die Implementierung in Klassen erfolgen muss, die von dieser abstrakten Klasse abgeleitet sind.

0
Sohail xIN3N