Freigeben über


Virtuelle Basisklassen

Da eine Klasse mehrfach als indirekte Basisklasse für eine abgeleiteten Klasse auftreten kann, stellt C++ eine Methode bereit, um die Arbeitsweise solcher Basisklassen zu optimieren. Virtuelle Basisklassen bieten eine Möglichkeit, Speicherplatz zu sparen und Mehrdeutigkeiten in Klassenhierarchien zu vermeiden, die Mehrfachvererbung verwenden.

Jedes nicht virtuelle Objekt enthält eine Kopie der Datenmember, die in der Basisklasse definiert sind. Durch diese Duplizierung wird Speicherplatz verschwendet, und Sie müssen bei jedem Zugriff angeben, welche Kopie der Basisklassenmember Sie wünschen.

Wenn eine Basisklasse als virtuelle Basisklasse angegeben ist, kann sie, ohne Verdoppelung der Datenmember, mehrmals als indirekte Basisklasse fungieren. Eine einzelne Kopie der Datenmember wird von allen Basisklassen, die diese als virtuelle Basis verwenden, gemeinsam genutzt.

Wenn eine virtuelle Basisklasse deklariert wird, wird in den Basislisten der abgeleiteten Klassen das virtual-Schlüsselwort angezeigt.

Betrachten Sie die Klassenhierarchie in der folgenden Abbildung, die eine simulierte Warteschlange veranschaulicht.

Diagramm mit Warteschlangensimulation

Diagramm der Warteschlangensimulation

In der Abbildung ist Queue die Basisklasse für CashierQueue und LunchQueue. Wenn jedoch beide Klassen kombiniert werden, um LunchCashierQueue zu bilden, tritt folgendes Problem auf: Die neue Klasse enthält zwei Unterobjekte des Typs Queue, eines von CashierQueue und das andere von LunchQueue. Die folgende Abbildung zeigt das konzeptuelle Speicherlayout (das tatsächliche Speicherlayout kann unter Umständen optimiert werden).

Simuliertes Warteschlangenobjekt

Simuliertes Warteschlangenobjekt

Beachten Sie, dass es zwei Queue-Unterobjekte im LunchCashierQueue-Objekt gibt. Der folgende Code deklariert Queue als virtuelle Basisklasse:

// deriv_VirtualBaseClasses.cpp
// compile with: /LD
class Queue {};
class CashierQueue : virtual public Queue {};
class LunchQueue : virtual public Queue {};
class LunchCashierQueue : public LunchQueue, public CashierQueue {};

Das virtual-Schlüsselwort stellt sicher, dass nur eine Kopie des Unterobjekts Queue enthalten ist (siehe folgende Abbildung).

Simuliertes Warteschlangenobjekt mit virtuellen Basisklassen

Simuliertes Warteschlangenobjekt, virtuelle Basisklassen

Eine Klasse kann eine virtuelle Komponente und eine nicht virtuelle Komponente eines bestimmten Typs haben. Dies geschieht unter den Bedingungen, die in der folgenden Abbildung veranschaulicht werden.

Virtuelle und nicht virtuelle Komponenten der gleichen Klasse

Virtuelle und nicht virtuelle Komponenten einer Klasse

In der Abbildung verwenden CashierQueue und LunchQueue Queue als virtuelle Basisklasse. Allerdings spezifiziert TakeoutQueue Queue als Basisklasse und nicht als virtuelle Basisklasse. Daher verfügt LunchTakeoutCashierQueue über zwei Unterobjekte des Typs Queue: eines aus dem Vererbungspfad, der LunchCashierQueue einschließt, und eines aus dem Pfad, der TakeoutQueue einschließt. Dies wird in der folgenden Abbildung verdeutlicht.

Objektlayout mit virtueller und nicht virtueller Vererbung

Virtuelle und nicht virtuelle Vererbung in Objektlayout

Hinweis

Virtuelle Vererbung bietet wesentliche Vorteile im Vergleich zur nicht virtuellen Vererbung.Allerdings kann es zu Mehraufwand bei der Verarbeitung kommen.

Wenn eine abgeleitete Klasse eine virtuelle Funktion überschreibt, die sie von einer virtuellen Basisklasse erbt, und wenn ein Konstruktor oder Destruktor der abgeleiteten Basisklasse die Funktion mithilfe eines Zeigers auf die virtuelle Basisklasse aufruft, führt der Compiler möglicherweise zusätzliche ausgeblendete "vtordisp"-Felder in die Klassen mit virtuellen Basen ein. Die /vd0-Compileroption unterdrückt das Hinzufügen des ausgeblendeten vtordisp-Destruktor-/Destruktorverschiebungsmember. Die /vd1-Compileroption, der Standard, ermöglicht diese, wo notwendig. Deaktivieren Sie vtordisps nur dann, wenn Sie sicher sind, dass alle Klassenkonstruktoren und -destruktoren virtuelle Funktionen virtuell aufrufen.

Die /vd- Compileroption wirkt sich auf das gesamte Kompilierungsmodul aus. Verwenden Sie das vtordisp-Pragma, um vtordisp-Felder zu unterdrücken und dann klassenweise wieder zu aktivieren:

#pragma vtordisp( off )
class GetReal : virtual public { ... };
#pragma vtordisp( on )

Siehe auch

Referenz

Mehrere Basisklassen