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...

19.4 Virtuell und Abstrakt

19.4 Virtuell und Abstrakt

Was hat es nun mit diesem virtuell und abstrakt auf sich? Zunächst einmal ist zu sagen, dass diese Begriffe in den unterschiedlichsten Zusammenhängen verwendet werden und oftmals das Gleiche meinen. Dies ist allerdings nicht korrekt. Spricht man von einer virtuellen Methode, so ist damit gemeint, dass es sich um eine Methode handelt, die in der Basisklasse implementiert ist, aber durch eine abgeleitete Klasse überschrieben werden kann, aber nicht muss. Man nutzt dies hauptsächlich, um bestehende Funktionalitäten zu erweitern. In meinem Beispiel sehen Sie so etwas an der Methode "Zeichnen". Auch wenn sie in der Klasse "CMotorrad" bzw. in der Klasse "CAuto" im Klassendiagramm nicht aufgeführt ist, so könnte sie in den Basisklassen die Farbe des "Zeichenstiftes" festlegen und andere vorbereitende Dinge erledigen. Die abgeleiteten Klassen könnten diese dann einfach nur aufrufen und sich allein auf das eigentliche Zeichnen beschränken. Somit spart man wieder redundanten Quelltext für jede der abgeleiteten Klassen. Dies sähe z.B. folgendermaßen aus.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
					
			
void CMotorrad::Zeichnen() {
	// Prüfen ob alle Komponenten erzeugt wurden
	// ...

	// Stiftfarben setzen und Hintergrund neu malen
	// ...
}

void CHarleyDavidson::Zeichnen() {
	// Aufruf der virtuellen Methode der Basisklasse
	CMotorrad::Zeichnen();

	// Motorrad Zeichnen
	// ...
}
					

In der Definition der Klassen, also in den Header-Dateien, signalisiert man dies, indem man vor die entsprechende Methode das Schlüsselwort "virtual" schreibt. Dies genügt in der Basisklasse, da man aber nie weiß, welche Klassen von der Aktuellen später noch abgeleitet werden sollen, welche wiederum das Zeichnen anders implementieren und die Funktionalität der Basisklasse nutzen möchten, schreibt man das "virtual" auch vor die Methodendefinition der aktuellen Klasse. Wenn eine Methode einmal als virtuell gekennzeichnet ist, wird sie in der gesamten nachfolgenden Vererbungshierarchie virtuell bleiben (man kann sie also immer überschreiben). Dessen sollten Sie sich immer bewusst sein.

Des weiteren ist es durchaus möglich, die virtuelle Methode, einer in der Hierarchie weiter oben liegenden Klasse aufzurufen, falls man die Funktionalität der dazwischenliegenden Klassen nicht braucht bzw. sie sogar störend sind. Man ruft aber nie die virtuellen Methoden aller abgeleiteten Klassen auf, da es logischerweise zu Dopplungen kommt. In diesem Beispiel kann die Klasse "CHarleyDavidson" aber nicht auf die Methode "Zeichnen" der Klasse "CFahrzeug" zugreifen, da jene nur abstrakt ist, was mich zum nächsten Thema führt.

Mit abstrakt ist gemeint, dass es in der Basisklasse eine formale Definition einer Methode gibt, welche jedoch nicht implementiert ist. Man möchte damit eine formale Regel definieren, welche besagt, dass es prinzipiell in einer abgeleiteten Klasse diese Methode geben kann und wenn dem so ist, dann sieht sie entsprechend aus und auf keinen Fall anders. Jede Methode, welche abstrakt definiert ist, ist somit auch virtuell. Manchmal verwendet man dann auch die Bezeichnung "virtuell abstrakt". Wie Sie eine solche abstrakte Methode definieren können, werde ich jetzt zeigen.

 1
 2
 3
 4
 5
					
class CFahrzeug {
	protected:
		// Formale Definition einer abstrakten Zeichnen-Methode
		void Zeichnen(void) = 0;
};
					

Die Syntax für abstrakte Methoden sieht etwas merkwürdig aus, da man fast glauben könnte, dass es eine Inlinedeklaration ist. Tatsächlich ist es auch im Prinzip so etwas. Man bringt dem Compiler so bei, dass er in der entsprechenden CPP Datei nicht nach einer solchen Methode Ausschau halten muss.

An dieser Stelle sei noch erwähnt, dass nur Methoden virtuell und oder abstrakt sind.

Wenn man von abstrakten Basisklassen spricht, dann meint man Klassen, welche virtuell abstrakte Methoden besitzen. Eine sehr wichtige Eigenschaft dieser Klassen ist, dass man von ihnen keine Objektinstanz erzeugen kann (eben weil die Implementierung bestimmter Methoden fehlt). Aber wenn man nur von C++ ausgeht, kann eine Klasse auch als abstrakt definiert werden, obwohl sie es theoretisch nicht ist (keine abstrakten Methoden). Durch einen speziellen Modifikator kann dies erzwungen werden. In meinem großen Beispiel habe ich diesen Fall nicht bedacht, aber dennoch möchte ich Ihnen kurz zeigen, wie man eine solche Klasse definiert.

 1
 2
 3
					
class CBasisKlasse abstract {
	// Definition von Attributen und Methoden
};
					

Dann gibt es da noch rein abstrakte Klassen (pure abstract), welche ausschließlich aus virtuell abstrakten Methoden bestehen und keinerlei Attribute definieren. Man spricht auch von s.g. Interfaces. Dem Klassennamen wird hier gerne ein "I" statt ein "C" vorangestellt. Gerade in Java kommen Interfaces sehr oft zum Einsatz. Hier sehen Sie schon, dass abstrakt mehrere Bedeutungen haben kann. Alle haben sie aber eines gemeinsam, man kann von ihnen keine Objektinstanz erzeugen.

Es gibt zu guter Letzt auch noch virtuelle Klassen. Was das ist, werde ich spä erklären, wenn es um Mehrfachvererbung geht.

An dieser Stelle möchte ich Sie gleich auf einen Nachteil der Vererbung hinweisen. Wenn man tatsächlich Methoden in einer Basisklasse als virtuell deklariert, hat dies zur Folge, dass Objektinstanzen größer werden, als man meint. Dies soll folgendes Beispiel demonstrieren.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
					
class CBasis1 {
	public:
		int m_iBasis1;
		virtual void TestMethode1() {}
};



class CBasis2 {
	public:
		int m_iBasis2;
		void TestMethode2() {};
};



class CKindVonBasis1 : public CBasis1 {};

class CKindVonBasis2 : public CBasis2 {};

// ...

printf("%i %i\n", sizeof(CKindVonBasis1), sizeof(CKindVonBasis2));
					

Ausgabe:

8 4
		

Wie Sie sehen können, sind die Basisklassen und die abgeleiteten Klassen vom Aufbau identisch, aber trotzdem haben die zwei Klassen "CKindVonBasis1" und "CKindVonBasis2" eine unterschiedliche Größe. Dies liegt daran, dass bei der Klasse "CKindVonBasis1" ein Zeiger auf eine s.g. virtuelle Tabelle hinzugefügt wird. Die ist zwingend notwendig, da man ja virtuelle Methoden überschreiben kann und sie somit mehrfach vorhanden sein können (eben in unterschiedlichen Kontexten). Damit der Computer also weiß, welche Methode er aufzurufen hat, wird eine Liste geführt (die genaue Funktionsweise erspare ich mir an dieser Stelle, aber falls Sie interessiert sind, suchen Sie doch mal im Internet nach "virtual table"). Der Verweis auf eine solche Tabelle kostet 4 Byte. Nun könnte man sich denken, dass das erst einmal nicht so schlimm ist, aber wenn man irgendwann ein Spiel entwickelt mit einer Million Polygonen, macht es schon einen Unterschied, ob man 4 MB mehr Speicher benötigt oder nicht. Abgesehen davon können Methoden auch nicht mehr direkt aufgerufen werden. Im Hintergrund muss immer erst die richtige Methode gesucht werden (abgesehen davon muss auch erst die virtuelle Tabelle geladen werden). Dies macht einen Methodenaufruf noch langsamer.

Zum Seitenanfang
Zum Inhaltsverzeichnis

© Copyright by Thomas Weiß, 2009 - 2012