Vererbung - virtual
-
Hallo!
Ich komme immer wieder damit durcheinander. Wann und wo verwendet man virtual?
Ich weiß - laut Scott Myers - man *muss immer* Basisklassen einen virtuellen Destruktor verpassen.Aber wann und wo sollten Methoden virtuell sein?
class Base { private: int m_int; public: Base() virtual ~Base() protected: // Diese Methode soll später in einer abgeleiteten Klasse überschrieben werden können, muss aber nicht int Foo (int n); // Diese Methode soll nicht überschrieben werden können void Foo1 (void); }; class TheClass : public Base { // Code };Braucht hier auch die Klasse "TheClass" einen virtuellen Destruktor?
Muss die Methode Foo, die nach Bedarf überschrieben werden darf in der Basisklasse schon als virtuell definiert werden oder erst in der abgeleiteten Klasse?Gruß Tim
-
Immer dann, wenn eine Methode überschreibbar sein soll, mußt du virtual hinschreiben.
In deinem Beispiel ist Foo virtual.
-
Wenn du vorhast, die Funktion irgendwann in einer abgeleiteten Klasse überschreiben zu wollen, mußt du sie virtuell definieren (das betrifft auch Foo1(). Andernfalls bekommst du Probleme mit der Typ-Auflösung (statischer Typ vs. dynamischer Typ).
Und daß jede Basisklasse einen virtuellen Destruktor benötigt, ist übertrieben - den brauchst du nur, wenn du Polymorphie mit Heap-Verwaltung kombinieren willst (ja, das trifft nahezu immer zu - aber der Unterschied ist wichtig ;))
PS: Ob du der abgeleiteten Klasse einen virtuellen Destruktor verpasst, ist egal - der wird automatisch virtuell, weil er in der Basis auch schon virtuell definiert wurde.
-
Gut, also alles was überschrieben werden soll, muss bereits in der Basisklasse als virtuell deklariert werden, richtig?
Danke, Tim.
-
Gut, also alles was überschrieben werden soll
Na, "soll" nicht gerade. "kann" wäre richtig.
-
gast1 schrieb:
Gut, also alles was überschrieben werden soll, muss bereits in der Basisklasse als virtuell deklariert werden, richtig?
Ein wenig ausführlicher:
Erstmal eine grobe Klassenstruktur
class Base{...}; class Class1 : public Base{...};Thema: Destruktor
int main() { Base* obj = new Class1(); delete obj; }Variante a: Base hat keinen virtuellen Destruktor
Nun wird der Destruktor von Base aufgerufen, nicht aber der, der abgeleiteten Klassen. Dies kann (muss aber nicht) zu Speicherleaks führen.Variante b: Base hat einen virtuellen Destruktor
Nun wird beim Destruktoraufruf sicher gestellt das alle Destruktoren des Objektes aufgerufen werden (von der untersten Klasse in Richtung Basisklasse).Anmerkung: Ich würde daher mich an Scott Mayers Rat halten. Den selbst wenn anfangs der virtuelle Destruktor nicht nötig sein sollte, kann eine Änderung an einer abgeleiteten Klasse dies bereits wieder ändern.
Thema: Virtuelle Funktionen
Nehmen wir einfach mal an wir haben in jeder der Klassen eine Methode foo mit der gleichen Signatur. z.B. void foo();int main() { Base* obj = new Class1(); obj->foo(); delete obj; }Variante a: Base hat foo nicht virtuell deklariert
Bei dem Aufruf wird Base::foo() aufgerufenVariante b: Base hat foo virtuell deklariert
Bei dem Aufruf wird Class1::foo() aufgerufenAnmerkung: Du musst dir hier einfach überlegen welche Funktionalität du erreichen willst, in der Regel ist es so das wenn eine Funktion überschreibbar sein sollte, diese auch virtuell deklariert sein muss.
Rein virtuelle Basisklasse
Von dir nicht erwähnt, ergänze ich dieses Thema aber dennoch der vollständigkeit halber. Wenn du eine Basisklasse hast, die nicht direkt benutzt werden soll, sondern wo man immer die abgeleiteten Klassen nutzen soll, musst du in der Basisklasse mindestens eine Methode rein virtuelle deklarieren. Diese Methode braucht dann auch nicht in der Basisklasse implementiert zu sein.Beispielsweise könnte man z.B. eine Basisklasse "Grafikobjekt" haben die eine rein virtuelle Methode "zeichen" hat (den wie etwas gezeichnet wird, ist wohl dem konkreten Grafikobjekt z.B. einem Rechteck bekannt, nicht aber einer abstrakten Klasse):
class Grafikobjekt { public: virtual void zeichne() = 0; // "pure virtual" ... };Nun kannst du keine Objekte des Typs Grafikobjekt direkt erzeigen, kannst aber weiterhin über den Zeiger auf abgeleitete Klassen zugreifen (sofern diese alle rein virtuellen Methoden überschrieben haben).
cu André
-
asc schrieb:
Variante a: Base hat keinen virtuellen Destruktor
Nun wird der Destruktor von Base aufgerufen, nicht aber der, der abgeleiteten Klassen. Dies kann (muss aber nicht) zu Speicherleaks führen.Nicht ganz - was hier passiert, ist technisch undefiniert, auch wenn die meisten Lösungen darauf hinauslaufen werden, daß nur der Basis-Dtor ausgeführt wird.
-
Destruktoren für Basis Klassen:
Entweder public virtual oder protected non- virtual (Herb Sutter in einem seiner Bücher...).
Simon