Twitter und Facebook-Anbindung
X
Tweet Follow @twitterapi
!!! Anbindung an twitter und facebook öffnen !!!

Wenn Ihnen mein Online-Buch gefällt,
dann bedanken Sie sich doch mit einer kleinen Spende...

22.3 Virtuelles Erben

22.3 Virtuelles Erben

Ich hatte schon einmal angedeutet, dass es möglich ist, auch virtuell zu erben, hatte aber nicht weiter erklärt, was damit gemeint ist und wozu es gut ist. Dies werde ich an dieser Stelle nachholen.

Sie hatten ja feststellen müssen, dass es mit der Mehrfachvererbung einige Schwierigkeiten gibt. Mit einer virtuellen Vererbung kann man ein paar dieser Schwierigkeiten umschiffen. Ein großer Nachteil war ja, dass es zu Dopplungen in abgeleiteten Klassen kommt, die bei einer diamantförmigen Vererbung störend ist. Wenn man aber die mittleren Klassen virtuell von der obersten Klasse erben lässt, hat man dieses Problem nicht und das Speicherlayout ähnelt dem, welches wir uns zuerst gewünscht hätten. Ich passe also das eben genannte Beispiel leicht an und das ganze sieht dann wie folgt aus.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
					
class CTransportmittel {
	public:
		int m_iPositionX;
		int m_iPositionY;
		int m_iPositionZ;
};



class CFahrzeug : public virtual CTransportmittel {
	public:
		int m_iReader;
};

class CFlugzeug : public virtual CTransportmittel {
	public:
		int m_iTriebwerke;
};

class CSchiff : public virtual CTransportmittel {
	public:
		int m_iSegel;
};



class CWasserFlugzeug : public CFahrzeug, public CFlugzeug, public CSchiff {
	public:
		int m_iPontons;
};

// ...

printf("CTransportmittel %i\n", sizeof(CTransportmittel));
printf("CFahrzeug        %i\n", sizeof(CFahrzeug));
printf("CFlugzeug        %i\n", sizeof(CFlugzeug));
printf("CSchiff          %i\n", sizeof(CSchiff));
printf("CWasserFlugzeug  %i\n", sizeof(CWasserFlugzeug));
					

Ausgabe:

CTransportmittel 12
CFahrzeug        20
CFlugzeug        20
CSchiff          20
CWasserFlugzeug  40
		

Wie Sie sehen, hat sich vom Quelltext her nicht viel geändert, aber die Größen der Klassen haben sich wesentlich verändert. Wie kommen die Werte Zustande? Die Größe der Klasse "CTransportmittel" ist gleich geblieben und somit auch nachvollziehbar, aber die Klassen "CFahrzeug", "CFlugzeug", "CSchiff" und "CWasserFlugzeug" sind auf einmal anders.

Die drei mittleren Klassen, welche virtuell erben, haben jetzt auch noch einen Pointer zusätzlich auf eine eigene virtuelle Tabelle hinzubekommen. Rechnen wir mal nach. Zwölf Byte für die Basisklasse plus drei mal acht Byte für die mittleren Klassen (Attribut plus VT-Pointer) sind zusammen 36. Jetzt sagte ich noch, dass auch die Klasse "CWasserFlugzeug" einen Pointer auf die VT bekommt, plus die vier Byte für das eigene Attribut. Da hätten wir eine Gesamtsumme von 42. Die Ausgabe hat aber gezeigt, dass die Klasse nur 40 Byte groß ist. Wie kann das sein? Um dies zu verstehen, habe ich in folgender Grafik das Speicherlayout vereinfacht dargestellt.

Speicherlayout bei einer virtuellen Vererbung

Wie Sie sehen können, teilen sich "CFahrzeug" und "CWasserFlugzeug" einen VT-Pointer. Dies ist für dieses einfache Beispiel möglich, denn der Visual Studio Compiler ist extrem effizient und optimiert so weit wie er kann. Mit anderen Compilern oder komplexeren Strukturen, kann die Sache aber schon ganz anders aussehen.

Sie merken also, dass das Speicherlayout sehr komplex werden kann und es ist auch meistens so, dass die Klassen nicht mehr zusammenhängend im Speicher stehen, was nicht gerade Cachefreundlich ist. Wir umgehen damit zwar jetzt die Dopplungen aber wir erkaufen uns dies durch zwei Nachteile. Zum einen kommen jetzt zusätzliche virtuelle Tabellen hinzu, welche wiederum Speicher benötigen und für jedem Zugriff eine Indirektionsstufe mehr benötigen (es wird langsamer). Zum anderen ist es jetzt nicht mehr möglich, eine Instanz der Klassen "CFahrzeug", "CFlugzeug" und "CSchiff" zu erzeugen, da sie jetzt abstrakte Klassen sind. Wie sieht es denn jetzt mit dem Polymorphismus aus? Der Upcast funktioniert wieder einwandfrei, aber der Downcast bleibt nach wie vor ein großes Problem und die Argumentation fällt wieder genauso aus. Es wird sogar noch schlimmer. Bei einer einfachen Mehrfachvererbung war es zu Mindestens möglich, mit Hilfe von Zeigern auf Objekte eine Hierarchieebene nach unten zu casten. Da die Positionsattribute jetzt aber in die oberste Basisklasse gewandert sind, ist auch dies jetzt nicht mehr möglich.

Das Fazit ist also, virtuelle Vererbung scheint zwar im ersten Augenblick die Lage zu entschärfen, aber bei näherem Betrachten fängt man sich noch mehr Ärger ein. Sie sollten sich also merken, dass egal wie Sie es anstellen, eine Mehrfachvererbung immer ungünstig ist. Meistens kann man diese Problematiken mit einer gekonnten Aggregation/Komposition umgehen. Die von mir genannten Beispiele hinken sowieso, da man sich bei Computerspielen eher für s.g. Leichtgewichtsobjekte entscheidet und das Zeichnen nicht den Klassen überlässt, sondern eine eigene Renderklasse dafür implementiert. Ich werde zu einem späteren Zeitpunkt diese Thematik wieder aufgreifen, wenn es um die Design Pattern geht.

Zum Seitenanfang
Zum Inhaltsverzeichnis

© Copyright by Thomas Weiß, 2009 - 2012