C++-Programmierung/ Objektorientierte Programmierung/ Abstrakte Klassen

Aus Wikibooks
Zur Navigation springen Zur Suche springen


Einleitung[Bearbeiten]

Abstrakte Klassen sind Klassen:

  • die nur aus einer Deklaration bestehen können.
  • von denen niemals Objekte erstellt werden. Keine Instanziierung möglich.
  • die oft als sog. Schnittstellen in umfangreichen Anwendungen verwendet werden.
  • die oft als Startpunkt(e) einer Vererbungshierarchie gedacht sind.

Deklarieren[Bearbeiten]

Eine Klasse wird dadurch abstrakt, indem man eine ihrer Mitgliedsmethoden als rein virtuell deklariert. Dazu schreiben Sie =0 hinter die Deklaration einer virtuellen Methode.

Nuvola-inspired-terminal.svg
1 class StatusAusgeber
2 {
3 public:
4 	virtual void printStatus(void) = 0;	// Rein virtuelle Deklaration
5         virtual ~StatusAusgeber() {}
6 };

Der Versuch, eine Instanz von dieser Klasse zu erstellen, schlägt fehl:

Crystal Clear action button cancel.svg
1 int main (void)
2 {
3 	StatusAusgeber instanz;  //Instanz von abstrakter Klasse kann nicht erstellt werden
4 	return 0; 
5 };

Verwenden[Bearbeiten]

Nun beschreiben wir die Verwendung von abstrakten Klassen anhand eines Beispiels. Dazu verwenden wir die abstrakte Klasse StatusAusgeber aus dem vorigen Abschnitt.

An gewissen Stellen Ihrer Programme wollen Sie die Funktionalität einer Klasse sicherstellen und verwenden, ohne auf die Funktionalitäten abgeleiteter Klassen eingehen zu müssen.

Wir deklarieren die Klassen Drucker und Bildschirm.

Nuvola-inspired-terminal.svg
 1 class Drucker : public StatusAusgeber
 2 {
 3 	unsigned int m_nDruckeAusgefuehrt;
 4 	unsigned int m_nLuefterAnzahl;
 5 	// Diverse druckerspezifische Attribute
 6 public:
 7 	// Diverse druckerspezifische Methoden
 8 	void printStatus(void)
 9 	{
10 		std::cout 
11 			<< "Geraet: Drucker" 
12 			<< std::endl
13 			<< "Drucke ausgefuehrt: " << m_nDruckeAusgefuehrt
14 			<< std::endl
15 			<< "Verbaute Luefteranzahl: " << m_nLuefterAnzahl
16 			<< std::endl;
17 	}
18 };
19 
20 class Bildschirm : public StatusAusgeber
21 {
22 	unsigned int m_nLeistungsaufnahmeWatt;
23 	unsigned int m_nDiagonaleAusdehnungZoll;
24 	// Diverse bildschirmspezifische Attribute
25 public:
26 	// Diverse bildschirmspezifische Methoden
27 	void printStatus(void)
28 	{
29 		std::cout 
30 			<< "Geraet: Bildschirm" 
31 			<< std::endl
32 			<< "Leistungsaufnahme (Watt): " << m_nLeistungsaufnahmeWatt
33 			<< std::endl
34 			<< "Bildschirmgroesse diagonal (Zoll): " << m_nDiagonaleAusdehnungZoll
35 			<< std::endl;
36 	}
37 };

Vorteil dieser Vorgehensweise ist die spätere Verwendung von Methoden der Basisklasse, bei denen die Implementierung erzwungen wurde. Der Verwender der abgeleiteten Klasse kann sich darauf verlassen, dass die Methode implementiert wurde, ohne die weiteren Teile der Hierarchie zu kennen.

Wir deklarieren die Klasse GeraeteMitStatusSpeicher

Nuvola-inspired-terminal.svg
 1 class GeraeteMitStatusSpeicher  : std::vector<StatusAusgeber*>
 2 {
 3 	static const char * _Trennzeile;	// Trennzeile zwischen den Statusangaben
 4 public:
 5 	void speichern(StatusAusgeber * GeraetMitStatus)
 6 	{
 7 		this->push_back(GeraetMitStatus); // Gerätezeiger im Vektor speichern
 8 	}
 9 	void printStatus(void)
10 	{
11 		std::vector<StatusAusgeber*>::const_iterator it = this->begin();
12 		while (it != this->end()) // Solange der Iterator nicht auf das Ende verweist
13 		{ // Iteration über den gesamten Inhalt
14 			(*it)->printStatus(); // Iterator dereferenzieren, enthaltenen Zeiger verwenden
15 			std::cout << _Trennzeile << std::endl; // Trennzeile ausgeben
16 			it++; // Nächsten möglichen Inhalt auswählen
17 		}
18 	}
19 };
20 const char * GeraeteMitStatusSpeicher::_Trennzeile = "---------------"; // statisch in GeraeteMitStatusSpeicher

GeraetMitStatusSpeicher ist von std::vector<StatusAusgeber*> abgeleitet, speichert Zeiger auf StatusAusgeber-Objekte.

Symbol opinion vote.svg
Hinweis

Dies ist möglich, da Zeiger eine feste Größe haben. Speicherung von Objekten abstrakter Klassen ist hier nicht möglich, da an dieser Stelle unmöglich die Größe der effektiven Objekte zu erkennen ist, auf die diese Zeiger verweisen. Genau das war uns ja von vornherein klar, weil wir nur an der printStatus()-Methode interessiert sind, deren Existenz durch die abstrakte Basisklasse sichergestellt wird. Egal ob hier ein Drucker, Monitor oder irgendein anderes Objekt hineingerät, das von StatusAusgeber abgeleitet wurde, wir können den Status ausgeben.

  • speichern(StatusAusgeber *) speichert einen Zeiger auf ein StatusAusgeber-Objekt
  • printStatus() ruft die Methode printStatus() für alle gespeicherten Zeiger auf StatusAusgeber-Objekte auf