Müssen ALLE virtuellen Funktionen in abgeleiteten Klassen implementiert werden?

89

Dies mag wie eine einfache Frage erscheinen, aber ich kann die Antwort nirgendwo anders finden.

Angenommen, ich habe Folgendes:

class Abstract {
public:
    virtual void foo() = 0;
    virtual void bar();
}

class Derived : Abstract {
public:
    virtual void foo();
}

Ist es in Ordnung, dass die Klasse Derived die Funktion bar () nicht implementiert? Was ist, wenn nicht ALLE meiner abgeleiteten Klassen die Funktion bar () benötigen, einige jedoch? Müssen alle virtuellen Funktionen einer abstrakten Basisklasse in den abgeleiteten Klassen implementiert werden oder nur diejenigen, die rein virtuell sind? Vielen Dank

mikestaub
quelle

Antworten:

81

Abgeleitete Klassen müssen nicht alle virtuellen Funktionen selbst implementieren . Sie müssen nur die reinen implementieren . 1 Das heißt, die DerivedKlasse in der Frage ist korrekt. Es erbt die barImplementierung von seiner Vorgängerklasse Abstract. (Dies setzt voraus, dass dies Abstract::barirgendwo implementiert ist. Der Code in der Frage deklariert die Methode, definiert sie jedoch nicht. Sie können sie inline definieren, wie Trenkis Antwort zeigt, oder Sie können sie separat definieren.)


1 Und selbst dann nur, wenn die abgeleitete Klasse instanziiert wird . Wenn eine abgeleitete Klasse nicht direkt instanziiert wird, sondern nur als Basisklasse von mehr abgeleiteten Klassen existiert, sind diese Klassen dafür verantwortlich, dass alle ihre reinen virtuellen Methoden implementiert werden. Die "mittlere" Klasse in der Hierarchie darf einige reine virtuelle Methoden nicht implementieren, genau wie die Basisklasse. Wenn die „mittlere“ Klasse hat eine rein virtuelle Methode implementieren, dann wird seine Nachkommen , dass die Umsetzung erben, so müssen sie es selbst nicht neu implementieren.

Rob Kennedy
quelle
3
Und selbst dies (Implementierung von reinen virtuellen Funktionen) nur, wenn sie instanziiert werden sollen (im Gegensatz dazu, dass sie selbst eine abstrakte Basisklasse sind).
Christian Rau
1
Das dachte ich mir. Aber mache ich das in meinem Projekt und erhalte einen Verknüpfungsfehler, der besagt, dass es ein "ungelöstes externes Symbol" für Derived :: bar () gibt; Aber ich habe in Derived nie einen Balken deklariert. Warum sucht der Linker nach dem Funktionskörper?
Mikestaub
@pixelpusher Natürlich Derived::barhat ein Funktionskörper, der von Abstract::bar. Es scheint also, dass die Übersetzungseinheit, in der dies definiert ist (ist sie überhaupt irgendwo definiert?), Nicht mit der Übersetzungseinheit verknüpft ist, in der sie aufgerufen wird.
Christian Rau
2
@ Rob: They only need to implement the pure ones.Es ist irreführend. Die abgeleiteten Klassen nicht unbedingt brauchen zu implementieren reinen entweder virtuellen Funktionen.
Nawaz
Ich schätze die Hilfe, aber @trenki hat den Nagel auf den Kopf getroffen. Obwohl Sie auch richtig waren, Christian Rau, wie es NICHT definiert wurde.
Mikestaub
46

Nur die rein virtuellen Methoden müssen in abgeleiteten Klassen implementiert werden, Sie benötigen jedoch noch eine Definition (und nicht nur eine Deklaration) der anderen virtuellen Methoden. Wenn Sie keinen angeben, kann sich der Linker sehr gut beschweren.

Wenn Sie also nur {}nach Ihrer optionalen virtuellen Methode setzen, erhalten Sie eine leere Standardimplementierung:

class Abstract {
public:
    virtual void foo() = 0; // pure virtual must be overridden
    virtual void bar() {}   // virtual with empty default implementation
};

class Derived : Abstract {
public:
    virtual void foo();
};

Eine komplexere Standardimplementierung würde jedoch in eine separate Quelldatei verschoben.

Trenki
quelle
7

Der ISO C ++ - Standard legt fest, dass alle virtuellen Methoden einer Klasse definiert werden müssen, die nicht rein virtuell sind.

Einfach ausgedrückt lautet die Regel:
Wenn Ihre abgeleitete Klasse die virtuelle Methode der Basisklasse überschreibt, sollte sie auch eine Definition bereitstellen. Wenn nicht, sollte die Basisklasse die Definition dieser Methode bereitstellen.

Benötigt gemäß der obigen Regel in Ihrem Codebeispiel virtual void bar();eine Definition in der Basisklasse.

Referenz:

C ++ 03 Standard: 10.3 Virtuelle Funktionen [class.virtual]

Eine in einer Klasse deklarierte virtuelle Funktion muss in dieser Klasse definiert oder als rein (10.4) deklariert werden, oder beides. Es ist jedoch keine Diagnose erforderlich (3.2).

Entweder sollten Sie die Funktion rein virtuell machen oder eine Definition dafür bereitstellen.

Die gcc- FAQ dokumentiert dies ebenfalls:

Der ISO C ++ - Standard legt fest, dass alle virtuellen Methoden einer Klasse, die nicht rein virtuell sind, definiert werden müssen, erfordert jedoch keine Diagnose für Verstöße gegen diese Regel [class.virtual]/8. Basierend auf dieser Annahme gibt GCC nur die implizit definierten Konstruktoren, den Zuweisungsoperator, den Destruktor und die virtuelle Tabelle einer Klasse in der Übersetzungseinheit aus, die ihre erste solche Nicht-Inline-Methode definiert.

Wenn Sie diese bestimmte Methode nicht definieren, kann sich der Linker daher über das Fehlen von Definitionen für scheinbar nicht verwandte Symbole beschweren. Um diese Fehlermeldung zu verbessern, muss möglicherweise der Linker geändert werden. Dies ist jedoch nicht immer möglich.

Die Lösung besteht darin, sicherzustellen, dass alle virtuellen Methoden definiert sind, die nicht rein sind. Beachten Sie, dass ein Destruktor definiert werden muss, auch wenn er als rein virtuell deklariert ist [class.dtor]/7.

Alok Speichern
quelle
3

Ja, das ist in Ordnung ... Sie müssen nur reine virtuelle Funktionen implementieren, um eine Klasse zu instanziieren, die von einer abstrakten Basisklasse abgeleitet ist.

Jason
quelle
1

Ja, es ist richtig, dass eine abgeleitete Klasse die Funktion überschreiben muss, die in der übergeordneten Klasse Pure Virtual ist. Die übergeordnete Klasse mit einer reinen virtuellen Funktion wird nur als abstrakte Klasse bezeichnet, da die untergeordnete Klasse einen eigenen Körper der reinen virtuellen Funktion angeben muss.

Für die normalen virtuellen Funktionen: - Es ist nicht erforderlich, sie weiter zu überschreiben, da einige untergeordnete Klassen diese Funktion haben können, andere möglicherweise nicht.

Der Hauptzweck des Mechanismus der virtuellen Funktion ist der Laufzeitpolymorphismus, unabhängig davon, ob der Hauptzweck der reinen virtuellen Funktion (abstrakte Klasse) darin besteht, die gleichnamige Funktion mit dem eigenen Körper obligatorisch zu machen.

CodeCodeCode
quelle