Programmierkurs C++ Abstrakte Klassen und Methoden Prof. Dr. Stefan Fischer Institut für Telematik, Universität zu Lübeck http://www.itm.uni-luebeck.de/people/fischer
#2 Vererbungshierarchie Obst double getpreisineuro(); Apfel Birne Kiwi double getpreisineuro(); double getpreisineuro(); double getpreisineuro();
#3 Problem dieser Lösung Methode double getpreisineuro(); In den Klassen Apfel, Birne und Kiwi implementiert Aber auch in der Klasse Obst Welchen Sinn hat die Implementierung in Obst?
Oberklasse Obst: getpreisineuro() Bisherige Implementierung komplett sinnlos Die Methode wird sowieso in den Unterklassen überschrieben Macht sogar Probleme, wenn man Instanzen von Obst verwendet (Negativer Preis) Eigentlich brauchen wir die Klasse nur, damit wir die Methode double getpreisineuro() in Obst haben #4
#5 Weiteres Problem dieser Lösung Wer erzwingt eigentlich, dass man in Unterklassen die Methode getpreisineuro() überschreibt? Der Compiler tut es nicht (siehe Beispiel unten) Erstellt man eine Instanz von Apfel, wird die Methode aus Obst aufgerufen Die Methode wurde ja nicht überschrieben
#6 Lösung: Abstrakte Klassen / pure virtual Methoden C++ verfügt über das Konzept der abstrakten Klassen Abstrakte Klassen haben mindestens eine pure virtual Methode Pure virtual Methoden haben keine Implementierung Eine Klasse mit pure virtual Methoden kann nicht instanziiert werden Abstrakte Klassen bieten somit eine Schnittstelle an, welche von den Unterklassen implementiert wird Erzwingt die Implementierung der Methode in den Unterklassen Syntax virtual method-signature = 0; Beispiel class A { virtual void f() = 0; }; class B { virtual void f(const int x&) const = 0; };
#7 Beispiel: Abstrakte Klasse Obst Abstrakte Klassen: pure virtual Methoden Syntax: virtual <Rückgabe> funktion(<parameter>) = 0; Beispiel:
Beispiel: Abstrakte Klasse Obst Von abstrakten Klassen können keine Instanzen erzeugt werden Löst das erste der beiden Probleme Erzeugen von Instanzen wird vom Compiler verhindert #8
#9 Beispiel: Abstrakte Methode in Obst Obst enthält nun eine abstrakte Methode Die Signatur der Methode ist für alle Unterklassen definiert Signatur reicht aus, um Methode aufzurufen Wie sie intern funktioniert, ist egal
#10 Beispiel: Abstrakte Methode in Obst Unterklassen erben die abstrakte Methode Daher: Compilerfehler Unterklasse soll nicht abstrakt sein: Sie muss alle abstrakten Methoden implementieren
#11 Abstract und Vererbung Abstrakte Methoden werden vererbt Damit eine Klasse instanziiert werden kann (also nicht abstrakt ist), müssen in der Vererbungshierarchie alle abstrakten Methoden mit Implementierungen überschrieben werden Wo dies geschieht ist dabei egal
Abstract und die Früchte Klassen Apfel, Birne und Kiwi sind nicht abstrakt Implementieren die abstrakte Methode aus Obst Darüber kann man erzwingen, dass Programmierer bestimmte Methoden implementieren Damit realisiert man eine Schnittstelle Auch API (Application Programming Interface) genannt #12
Abstract und nicht-abstract mischen Nicht abstrakte Methoden können abstrakte Methoden verwenden Möglich, da bei nicht-abstrakten Instanzen von Unterklassen die Methode ja implementiert sein muss Damit können bestimmte Elemente der Implementierung offen gelassen werden Vorteil: Verhalten, das bei allen Unterklassen sehr ähnlich ist, kann in die Oberklasse verschoben werden #13
Beispiel (siehe nächste Folie) Beide Klassen implementieren die Methode getname unterschiedlich Methode tuewasmit(gutentag g) in der Klasse Main kennt nur das Interface von GutenTag Kann damit getname() oder stelldichvor() aufrufen stelldichvor() ruft getname der jeweiligen Instanz auf #14
#15
Interfaces In C++ können Klassen von mehreren Oberklassen erben Sogenannte Mehrfachvererbung In anderen Programmiersprachen ist dies nicht möglich (z.b. Java) Als Alternative werden sogenannte Interfaces angeboten Diese existieren in C++ nicht als eigenes Konstrukt / Schlüsselwort, sie können jedoch konzeptionell umgesetzt werden Ein Interface enthält lediglich Methodensignaturen D.h. keine Methodenimplementierung / kein Code In C++ kann dies durch eine Klasse umgesetzt werden, die ausschließlich abstrakte Methoden enthalten #16
Interfaces: Obstbeispiel Bei manchen Obstsorten ist Herkunftsland wichtig Methode Land getherkunftsland(); Soll (wenn bekannt) von Obstsorten implementiert werden Beispiel Kiwi Klasse Kiwi erbt von Obst und Herkunftsland #17
#18 Interfaces: Obstbeispiel Daher: Neue abstracte Klasse ( Interface ) Herkunftsland class Herkunftsland { public: Land* getherkunftsland() = 0; } Klasse Kiwi implementiert das Interface class Kiwi: public Obst, public Herkunftsland { Land* getherkunftsland() { return new Land( Neuseeland ); } }
Vererbungshierarchie Kiwi ist-ein Obst Herkunftsland Instanzen vom Typ Kiwi können per Typecast auf Obst und Herkunftsland abgebildet werden Obst Herkunftsland Beispiel Obst* obst = new Kiwi(); Herkunftsland* land = new Kiwi(); Kiwi Obst* obst2 = (Obst*) land; Herkunftsland* land2 = (Herkunftsland*) obst; #19
Zusammenfassung Abstrakte Klassen können nicht direkt instanziiert werden Abstrakte Methoden Erzwingen, dass in Unterklassen diese Methode implementiert werden muss, damit diese nicht mehr abstrakt sein müssen Resultat: Gemeinsame Schnittstelle bzw. Interface Interfaces Interfaces sind abstrakte Klassen mit ausschließlich abstrakten Methoden #20