Suche…


Syntax

  • virtuelle Leere f ();

  • virtuelle Leere g () = 0;

  • // C ++ 11 oder höher:

    • virtuelle Leere h () überschreiben;
    • void i () überschreiben;
    • virtuelle Leere j () final;
    • ungültig k () final;

Bemerkungen

Verwenden der Überschreibung mit virtual in C ++ 11 und höher

Das override hat in C ++ 11 eine besondere Bedeutung, wenn es am Ende der Funktionssignatur angehängt wird. Dies bedeutet, dass eine Funktion ist

  • Überschreiben der Funktion in der Basisklasse &
  • Die Basisklassenfunktion ist virtual

Dieser Spezifizierer hat keine run time da er hauptsächlich als Hinweis für Compiler gedacht ist

Das folgende Beispiel zeigt die Änderung des Verhaltens bei Verwendung ohne Überschreiben.

Ohne override :

#include <iostream>

struct X {
    virtual void f() { std::cout << "X::f()\n"; }
};

struct Y : X {
    // Y::f() will not override X::f() because it has a different signature,
    // but the compiler will accept the code (and silently ignore Y::f()).
    virtual void f(int a) { std::cout << a << "\n"; }
};

Mit override :

#include <iostream>

struct X {
    virtual void f() { std::cout << "X::f()\n"; }
};

struct Y : X {
    // The compiler will alert you to the fact that Y::f() does not
    // actually override anything.
    virtual void f(int a) override { std::cout << a << "\n"; }
};

Beachten Sie, dass override kein Schlüsselwort ist, sondern ein spezieller Bezeichner, der nur in Funktionssignaturen vorkommen kann. In allen anderen Kontexten kann override noch als Bezeichner verwendet werden:

void foo() {
    int override = 1; // OK.
    int virtual = 2;  // Compilation error: keywords can't be used as identifiers.
}

Virtuelle vs. nicht virtuelle Memberfunktionen

Mit virtuellen Memberfunktionen:

#include <iostream>

struct X {
    virtual void f() { std::cout << "X::f()\n"; }
};

struct Y : X {
    // Specifying virtual again here is optional
    // because it can be inferred from X::f().
    virtual void f() { std::cout << "Y::f()\n"; } 
};

void call(X& a) {
    a.f();
}

int main() {
    X x;
    Y y;
    call(x); // outputs "X::f()"
    call(y); // outputs "Y::f()"
}

Ohne virtuelle Memberfunktionen:

#include <iostream>

struct X {
   void f() { std::cout << "X::f()\n"; }
};

struct Y : X {
   void f() { std::cout << "Y::f()\n"; }
};

void call(X& a) {
    a.f();
}

int main() {
    X x;
    Y y;
    call(x); // outputs "X::f()"
    call(y); // outputs "X::f()"
}

Letzte virtuelle Funktionen

In C ++ 11 wurde der final Bezeichner eingeführt, der das Überschreiben von Methoden verbietet, wenn sie in der Methodensignatur erscheinen:

class Base {
public:
    virtual void foo() {
        std::cout << "Base::Foo\n";
    }
};

class Derived1 : public Base {
public:
    // Overriding Base::foo
    void foo() final {
        std::cout << "Derived1::Foo\n";
    }
};

class Derived2 : public Derived1 {
public:
    // Compilation error: cannot override final method
    virtual void foo() {
        std::cout << "Derived2::Foo\n";
    }
};

Der Spezifizierer final kann nur mit der "virtuellen" Member-Funktion verwendet werden und kann nicht auf nicht-virtuelle Member-Funktionen angewendet werden

Wie final gibt es auch einen Spezifizierer, der das Überschreiben von virtual Funktionen in der abgeleiteten Klasse verhindert.

Die Spezifizierer override und final können kombiniert werden, um die gewünschte Wirkung zu erzielen:

class Derived1 : public Base {
public:
    void foo() final override {
        std::cout << "Derived1::Foo\n";
    }
};

Verhalten virtueller Funktionen in Konstruktoren und Destruktoren

Das Verhalten von virtuellen Funktionen in Konstruktoren und Destruktoren ist beim ersten Auftreten oft verwirrend.

#include <iostream>
using namespace std;

class base { 
public:
    base() { f("base constructor"); }
    ~base() { f("base destructor"); }

    virtual const char* v() { return "base::v()"; }

    void f(const char* caller) { 
        cout << "When called from " << caller << ", "  << v() << " gets called.\n"; 
    }        
};

class derived : public base {
public:
    derived() { f("derived constructor"); }
    ~derived() { f("derived destructor"); }

    const char* v() override { return "derived::v()"; }

};

int main() {
     derived d;
}

Ausgabe:

Beim Aufruf vom Basiskonstruktor wird base :: v () aufgerufen.
Beim Aufruf aus dem abgeleiteten Konstruktor wird die abgeleitete :: v () - Anweisung aufgerufen.
Wenn der abgeleitete Destruktor aufgerufen wird, wird der abgeleitete :: v () aufgerufen.
Beim Aufruf vom Basis-Destruktor wird base :: v () aufgerufen.

Der Grund dafür ist, dass die abgeleitete Klasse zusätzliche Member definieren kann, die noch nicht initialisiert sind (im Konstruktorfall) oder bereits zerstört wurden (im Destruktorfall). Daher wird der dynamische Typ von *this beim Konstruieren und Zerstören von C ++ - Objekten als Klasse des Konstruktors oder Destruktors und nicht als eine abgeleitete Klasse betrachtet.

Beispiel:

#include <iostream>
#include <memory>

using namespace std;
class base {
public:
    base()
    {
        std::cout << "foo is " << foo() << std::endl;
    }
    virtual int foo() { return 42; }
};

class derived : public base {
    unique_ptr<int> ptr_;
public:
    derived(int i) : ptr_(new int(i*i)) { }
    // The following cannot be called before derived::derived due to how C++ behaves, 
    // if it was possible... Kaboom!
    int foo() override   { return *ptr_; } 
};

int main() {
    derived d(4);
}

Reine virtuelle Funktionen

Wir können auch angeben, dass eine virtual Funktion rein virtuell (abstrakt) ist, indem Sie an die Deklaration = 0 anhängen. Klassen mit einer oder mehreren reinen virtuellen Funktionen werden als abstrakt betrachtet und können nicht instanziiert werden. Es können nur abgeleitete Klassen instanziiert werden, die Definitionen für alle rein virtuellen Funktionen definieren oder erben.

struct Abstract {
    virtual void f() = 0;
};

struct Concrete {
    void f() override {}
};

Abstract a; // Error.
Concrete c; // Good.

Selbst wenn eine Funktion als rein virtuell angegeben ist, kann eine Standardimplementierung angegeben werden. Trotzdem wird die Funktion als abstrakt betrachtet und abgeleitete Klassen müssen sie definieren, bevor sie instanziiert werden können. In diesem Fall darf die Version der abgeleiteten Klasse der Funktion sogar die Version der Basisklasse aufrufen.

struct DefaultAbstract {
    virtual void f() = 0;
};
void DefaultAbstract::f() {}

struct WhyWouldWeDoThis : DefaultAbstract {
    void f() override { DefaultAbstract::f(); }
};

Es gibt einige Gründe, warum wir dies tun möchten:

  • Wenn wir eine Klasse erstellen möchten, die nicht selbst instanziiert werden kann, die instanziierten Klassen jedoch nicht davon abgehalten wird, können wir den Destruktor als rein virtuell deklarieren. Als Destruktor muss er auf jeden Fall definiert werden, wenn die Instanz freigegeben werden soll. Da der Destruktor höchstwahrscheinlich bereits virtuell ist, um Speicherverluste während der polymorphen Verwendung zu verhindern , wird es nicht unnötig sein, die Performance einer anderen virtual Funktion zu deklarieren. Dies kann bei der Erstellung von Schnittstellen hilfreich sein.

      struct Interface {
          virtual ~Interface() = 0;
      };
      Interface::~Interface() = default;
    
      struct Implementation : Interface {};
      // ~Implementation() is automatically defined by the compiler if not explicitly
      //  specified, meeting the "must be defined before instantiation" requirement.
    
  • Wenn die meisten oder alle Implementierungen der reinen virtuellen Funktion doppelten Code enthalten, kann dieser Code stattdessen in die Basisklassenversion verschoben werden, wodurch die Pflege des Codes vereinfacht wird.

      class SharedBase {
          State my_state;
          std::unique_ptr<Helper> my_helper;
          // ...
    
        public:
          virtual void config(const Context& cont) = 0;
          // ...
      };
      /* virtual */ void SharedBase::config(const Context& cont) {
          my_helper = new Helper(my_state, cont.relevant_field);
          do_this();
          and_that();
      }
    
      class OneImplementation : public SharedBase {
          int i;
          // ...
    
        public:
          void config(const Context& cont) override;
          // ...
      };
      void OneImplementation::config(const Context& cont) /* override */ {
          my_state = { cont.some_field, cont.another_field, i };
          SharedBase::config(cont);
          my_unique_setup();
      };
    
      // And so on, for other classes derived from SharedBase.
    


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow