Vererbung und Polymorphie WiMa-Praktikum 1, Teil C++, Tag 5 Christoph Ott, Büro: Helmholtzstr.18, E22 Tel.: 50-23575, Mail: christoph.ott@uni-ulm.de Institut für Angewandte Informationsverarbeitung 29.08.08 Christoph Ott 1
Übersicht: Vererbung und Polymorphie Vererbung Prinzip Kennzeichnung Vorteile virtuelle Funktionen & Polymorphie Überladen von Elementfunktionen Compile-Zeit vs. Laufzeit Das Schlüsselwort virtual abstrakte Klassen nicht-instanziierbare Klassen rein virtuelle Funktionen Mehrfachvererbung im Unterschied zu Java möglich 29.08.08 Christoph Ott 2
Vererbung Prinzip: Unterklassen werden von Oberklassen abgeleitet Unterklasse besitzt automatisch sämtliche Funktionalität der Oberklasse (Voraussetzung: Variablen und Funktionen sind protected oder public deklariert) Unterklasse erweitert Funktionalität der Oberklasse Kennzeichnung: nach Klassenname durch : getrennt Name der Oberklasse angeben Bsp.: class A:public B {... }; Vorteile: Vererbungsbeziehungen der realen Welt können ausgedrückt werden (Porsche ist ein ganz bestimmtes Auto) reduzierter Schreibt-/ Tippaufwand (Code der Oberklasse muss nicht mehr abgeschrieben werden) Wiederverwendung bereits existierender Oberklassen Änderung bei Fehlern nur in einer Klasse 29.08.08 Christoph Ott 3
Überladen von Elementfunktionen Bisher: Elementfunktion einer Klasse kann durch unterschiedliche Parameterdeklaration überladen werden Elementfunktion einer Klasse kann von der einer anderen Klasse überladen werden Neu: Elementfunktion einer Oberklasse kann in einer Unterklasse überladen werden. Problem: Klasse des Objekts kann zur Compilezeit noch nicht feststehen. Polymorphismus: Fähigkeit, zur Laufzeit auf Basis des Objekts zu entscheiden, welche der Elementfunktionen (Ober-/ Unterklasse) aufgerufen werden soll (späte Bindung!) Default in C++ ist jedoch frühe Bindung Lösung: Schlüsselwort virtual teilt c++ mit, dass eine Funktion polymorph verwendet werden soll / spät gebunden werden soll 29.08.08 Christoph Ott 4
virtuelle Funktionen Voraussetzungen für späte Bindung: Elementfunktion in Unterklasse muss identisch deklariert sein (inklusive Rückgabetyp) Elementfunktion darf nicht statisch sein. Elementfunktion darf nicht inline definiert sein. Konstruktoren können nicht virtuell deklariert werden. Jedoch: Destruktor (bisher nicht bekannt) muss virtuell deklariert werden (virtual ~Konto(){} in Header-Datei). rein virtuelle Funktionen: werden in Oberklasse nur deklariert (d.h. nicht implementiert), müssen also zwangsläufig überladen werden Schreibweise: virtual double einzahlen() = 0; Klassen mit mind. einer rein virtuellen Funktion heißen abstrakte Klassen und sind (da nicht vollständig definiert) nicht instanziierbar 29.08.08 Christoph Ott 5
Verwendung von abstrakten Klassen: 6 Goldene Regeln 1. Sollen die Unterklassen auf die Elementvariablen zugreifen können, so müssen diese als protected ausgezeichnet werden. 2. Destruktor, virtuelle Funktionen und rein virtuelle Funktionen müssen in der Header-Datei als virtuell deklariert werden. 3. Alle virtuellen und rein virtuellen Funktionen müssen in jeder Unterklasse deklariert und definiert werden. 4. Objekte einer abstrakten Klasse können nicht erzeugt werden. Anstatt dessen muss man Zeiger auf eine abstrakte Klasse erzeugen. Diese können dann polymorph verwendet werden. 5. Wenn man Elementfunktionen von Zeigern aufruft, muss man mit -> anstatt mit. arbeiten. 6. Ebenso müssen bei der polymorphen Übergabe von Objekten an (Element-) Funktionen, stets Zeiger anstatt Referenzen verwendet werden. 29.08.08 Christoph Ott 6
Beispiel: Objektorientierung Konto double stand; int frist; boolean einzahlen (double betrag); boolean abheben (double betrag); int nr;??? Girokonto stand>=-500 abheben: betrag <=4000 Christoph Steffen Norbert Wolfgang Sparbuch abheben: geht nicht! Christoph Wolfgang Armin Strumpf stand >=0 Gerhard Franz 29.08.08 Christoph Ott 7
Beispiel: Konto als abstrakte Klasse - include etc. - class Konto{ protected: string inhaber; int nr; double stand; konto.h public: - nicht-virtuelle Fkt. - virtual ~Konto(){} virtual int getnr(); virtual bool setnr(int nummer); virtual bool abheben (double betrag) = 0; }; class Giro:public Konto{ public: Giro(string i); bool abheben(double betrag); };... #include konto.h konto.cpp - sonstige Fkt. - bool Konto::ueberweisen (Konto* empf, double sum){ if (abheben(sum)){ empf->einzahlen(sum); return true; } return false; } Giro::Giro(string i){ inhaber=i; nr=n; stand=s; } bool Giro::abheben(double betrag){ if ((betrag<=3000) && (stand-betrag)>=-500){ stand-=betrag; return true;} return false; }... #include konto.h kontofuehrung.cpp Konto *giro, *socke, *spar; giro = new Giro("Chris"); spar = new Sparbuch("Tom"); socke = new Strumpf("Tom"); giro->ueberweisen(spar, 100); spar->abheben(200); socke->einzahlen(300); cout << giro->getstand() << endl; cout << spar->getstand() << endl; cout << socke->getstand() << endl; 29.08.08 Christoph Ott 8