C++ Programmierkurs - ZAIK
Transcrição
C++ Programmierkurs - ZAIK
1 C++ Programmierkurs 3 6. Vererbung und Polymorphismus Wintersemester 04 / 05 6.1 6.2 6.3 6.4 6.5 Christian Hagemeier Lehrstuhl Prof. Dr. Schrader Institut für Informatik Universität zu Köln Einführung in Vererbung Vererbung in C++ Grundlagen des Polymorphismus Abstrakte Klassen Virtuelle Destruktoren mittwochs 17–18:30 Uhr Hörsaal II der Physikalischen Institute http://www.zaik.de/AFS/teachings/ Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader 2 4 Gliederung der Vorlesung 6.1 Einführung in Vererbung 20.10.: Organisatorisches und Einführung 27.10.: Einführung und Kontrollstrukturen 03.11.: Kontrollstrukturen 10.11.: Kontrollstrukturen und Funktionen 17.11.: Funktionen 24.11.: Vektoren, Zeiger 01.12.: Zeiger, Strings 08.12.: Klassen (1) 15.12.: Klassen (2), Überladen von Operatoren 22.12.: Vererbung und Polymorphismus 12.01.: Datei und Stream Ein–/Ausgabe, Diverses • Wiederverwendbarkeit • Erzeugen einer Klasse aus existierender Basisklasse Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader – "erbt" Daten und Verhalten der Basisklasse – "Verfeinerung" mit weiteren Details • stärker spezialisierte Gruppe an Objekten wird abgebildet • Verhalten der Ursprungsklasse kann modifiziert werden • Vererbung über mehrere Stufen möglich (indirekte Vererbung) • Mehrfachvererbung möglich (Vererbung von mehreren Basisklassen) Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader 1 5 Beziehungen im Objektmodell 7 Vererbungshierarchie: Beispiel 1 • Abstraktion CommunityMember – zielt auf Gemeinsamkeiten der Klassen im Modell • "ist ein"-Beziehung – wird im Objektmodell durch Vererbung abgebildet – Objekte der abgeleiteten Klasse können als Objekte der Basisklasse betrachtet werden – Beispiel Husky ist ein Hund: Huskies besitzen alle Eigenschaften eines Hundes Employee Faculty Student Staff Alumnus Single inheritance Single inheritance • "hat ein"-Beziehung – Komposition: Klassenattribut von diesem Typ – Objekt enthält ein oder mehrere Objekte anderer Klassen als Variablen – Beispiel Hund hat einen Kopf Programmierkurs C++ – WS 04/05 Teacher Single inheritance AdministratorTeacher Multiple inheritance Administrator Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader 6 Basisklasse und abgeleitete Klasse 8 Vererbungshierarchie: Beispiel 2 • Basisklasse spiegelt typischerweise größere Objektmenge wieder als abgeleitete Klasse • Beispiele: Basisklasse Student Shape Loan Employee Account Programmierkurs C++ – WS 04/05 abgeleitete Klassen GraduateStudent UndergraduateStudent Circle Triangle Rectangle CarLoan HomeImprovementLoan MortgageLoan FacultyMember StaffMember CheckingAccount SavingsAccount Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader Shape TwoDimensionalShape Circle Square Programmierkurs C++ – WS 04/05 Triangle ThreeDimensionalShape Sphere Cube Tetrahedron Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader 2 9 11 6.2 Vererbung in C++ Zugriffssteuerung protected • 3 Arten der Vererbung: • Zwischenstufe zwischen public und private • Zugriff auf protected Klassenmitglieder – public • Jedes Objekt der abgeleiteten Klasse ist auch Objekt der Basisklasse • Objekte der Basisklasse sind keine Objekte der abgeleiteten Kl. • Beispiel: Alle Huskies sind Hunde, nicht alle Hunde sind Huskies • Nichtprivate Elemente der Basisklasse können verwendet werden (durch vererbte nicht-private Methoden) – wie bei private und protected: • durch Basisklassenmethoden • durch friend-Funktionen der Basisklasse – Schärfer als public, laxer als private: • durch Methoden der abgeleiteten Klasse • durch friend-Funktionen der abgeleiteten Klasse – private • Zugriff nicht möglich von externen Klassen • Nutzung der public- und protected-Methoden von Basisklassen: einfach Methodenname verwenden! • Alternative zu Komposition (siehe "hat ein"-Beziehung) – protected • selten genutzt Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader 10 12 public-Vererbung Aufteilung des Quellcodes auf Dateien (1) • Syntax: • Headerdateien class TwoDimensionalShape : public Shape – Klasse TwoDimensionalShape erbt von Klasse Shape • private Elemente der Basisklasse: – sind nicht direkt verfügbar – werden trotzdem mit vererbt (Zugriff über entsprechende Methoden) • public- und protected-Methoden und -Variablen werden so "übernommen" • friend-Funktionen werden nicht vererbt Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader – Deklarationen von globalen Variablen – Prototypen von Funktionen – Deklaration von Klassen mit Methodenprototypen • Implementationsdateien – Implementation der Funktionen und Klassen • Insgesamt eine main()-Funktion in allen Implementationsdateien • Problem: Sicherstellen, daß jede Deklaration nur einmal vorgenommen wird • Lösung: Präprozessoranweisungen – #ifndef <marke>: wenn <marke> nicht definiert ist, binde folgenden Code ein, sonst überspringe ihn – #define <marke>: definiert <marke> – #endif: Gegenstück zu vorangegangenen #ifndef Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader 3 13 15 Aufteilung des Quellcodes auf Dateien (2) Beispiel "Punkt": Implementationsdatei (1) • Genereller Aufbau: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #ifndef _test_h_ #define _test_h_ // hier kommt der Quellcode mit den // Deklarationen hin #endif • Einbinden der Deklaration vor Nutzung/Definition mittels #include "dateiname" • Vorgehen beim Kompilieren – Jede Implementationsdatei wird vom Compiler separat in Objektcode übersetzt – Diese Objektcode-Dateien werden vom Linker zum ausführbaren Programm zusammengebunden Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader // Fig. 9.13: point2.cpp // Point2 class member-function definitions. #include <iostream> using std::cout; #include "point2.h" // Point2 class definition // default constructor Point2::Point2( int xValue, int yValue ) { x = xValue; y = yValue; } // end Point2 constructor // set x in coordinate pair void Point2::setX( int xValue ) { x = xValue; // no need for validation } // end function setX Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader 14 16 Beispiel "Punkt": (Headerdatei) Beispiel "Punkt": Implementationsdatei (2) 1 2 3 4 5 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 23 24 25 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 // Fig. 9.12: point2.h // Point2 class definition represents an x-y coordinate pair. #ifndef POINT2_H #define POINT2_H class Point2 { public: Point2( int = 0, int = 0 ); // default constructor void setX( int ); int getX() const; // set x in coordinate pair // return x from coordinate pair void setY( int ); int getY() const; // set y in coordinate pair // return y from coordinate pair void print() const; // output Point2 object protected: int x; // x part of coordinate pair int y; // y part of coordinate pair }; // end class Point2 // return x from coordinate pair int Point2::getX() const { return x; } // end function getX // set y in coordinate pair void Point2::setY( int yValue ) { y = yValue; // no need for validation } // end function setY // return y from coordinate pair int Point2::getY() const { return y; } // end function getY #endif Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader 4 17 19 Beispiel "Punkt": Implementationsdatei (3) Beispiel "Kreis": Implementationsdatei (1) 45 46 47 48 49 50 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 // output Point2 object void Point2::print() const { cout << '[' << x << ", " << y << ']'; } // end function print Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader // Fig. 9.15: circle3.cpp // Circle3 class member-function definitions. #include <iostream> using std::cout; #include "circle3.h" // Circle3 class definition // default constructor Circle3::Circle3( int xValue, int yValue, double radiusValue ) { x = xValue; y = yValue; setRadius( radiusValue ); } // end Circle3 constructor // set radius void Circle3::setRadius( double radiusValue ) { radius = ( radiusValue < 0.0 ? 0.0 : radiusValue ); } // end function setRadius Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader 18 Beispiel "Kreis": Headerdatei 1 // Fig. 9.14: circle3.h 2 // Circle3 class contains x-y coordinate pair and radius. 3 #ifndef CIRCLE3_H 4 #define CIRCLE3_H 5 6 #include "point2.h" // Point2 class definition 7 8 class Circle3 : public Point2 { 10 public: 12 // default constructor 13 Circle3( int = 0, int = 0, double = 0.0 ); 14 15 void setRadius( double ); // set radius 16 double getRadius() const; // return radius 17 18 double getDiameter() const; // return diameter 19 double getCircumference() const; // return circumference 20 double getArea() const; // return area 21 22 void print() const; // output Circle3 object 24 private: 25 double radius; // Circle3's radius 27 }; // end class Circle3 29 #endif Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader 20 Beispiel "Kreis": Implementationsdatei (2) 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 // return radius double Circle3::getRadius() const { return radius; } // end function getRadius // calculate and return diameter double Circle3::getDiameter() const { return 2 * radius; } // end function getDiameter // calculate and return circumference double Circle3::getCircumference() const { return 3.14159 * getDiameter(); } // end function getCircumference Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader 5 21 Beispiel "Kreis": Implementationsdatei (3) 46 47 48 49 50 51 52 53 54 55 56 57 58 59 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader 24 circle.setX( 2 ); // set new x-coordinate 25 circle.setY( 2 ); // set new y-coordinate 26 circle.setRadius( 4.25 ); // set new radius 27 28 // display new point value 29 cout << "\n\nThe new location and radius of circle are\n"; 30 circle.print(); 31 32 // display floating-point values with 2 digits of precision 33 cout << fixed << setprecision( 2 ); 34 35 // display Circle3's diameter 36 cout << "\nDiameter is " << circle.getDiameter(); 37 38 // display Circle3's circumference 39 cout << "\nCircumference is " << circle.getCircumference(); 40 41 // display Circle3's area 42 cout << "\nArea is " << circle.getArea(); 43 44 cout << endl; 45 46 return 0; // indicates successful termination 48 } // end main Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader 22 24 // calculate and return area double Circle3::getArea() const { return 3.14159 * radius * radius; } // end function getArea // output Circle3 object void Circle3::print() const { cout << "Center = [" << x << ", " << y << ']' << "; Radius = " << radius; } // end function print Programmierkurs C++ – WS 04/05 23 Beispiel: main()-Funktion (2) Beispiel: main()-Funktion (1) Ausgabe des Beispiels 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 // Fig. 9.16: circletest3.cpp // Testing class Circle3. #include <iostream> X coordinate is 37 Y coordinate is 43 Radius is 2.5 using std::cout; using std::endl; using std::fixed; The new location and radius of circle are Center = [2, 2]; Radius = 4.25 Diameter is 8.50 Circumference is 26.70 Area is 56.74 #include <iomanip> using std::setprecision; #include "circle3.h" // Circle3 class definition int main() { Circle3 circle( 37, 43, 2.5 ); // instantiate Circle3 object // display point coordinates cout << "X coordinate is " << circle.getX() << "\nY coordinate is " << circle.getY() << "\nRadius is " << circle.getRadius(); Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader 6 25 27 Verwendung von protected 6.3 Grundlagen des Polymorphismus • Vorteile: • Polymorphismus: Auswahl der auszuführenden Aktion wird dynamisch durch Klasse bestimmt – abgeleitete Klassen können Werte direkt modifizieren – etwas schneller (Vermeidung der Verwendung von get-/setFunktionen) • Kernkonzept: Abgeleitete Klassen können als Instanzen der Basisklasse behandelt werden • Nachteile: – Zulässigkeitstest innerhalb der Basisklasse wird umgangen • Dadurch: Nahtloses Einfügen neuer Funktionalität/ Klassen in existierendes Modell möglich • abgeleitete Klasse kann illegale Werte zuweisen – Implementationsabhängigkeit – Neue Klasse können problemlos hinzugefügt werden und werden automatisch wie bisherige Klassen berücksichtigt • Methoden der abgeleiteten Klasse sind meist abhängiger von der Implementation der Basisklasse • Modifikation der Basisklasse kann konsequenzen auf abgeleitete Klasse haben (fragile Software) • Aufruf einer Methode, die in abgeleiteter Klasse überladen wurde: – Bisher: Auswahl der ausgeführten Funktion anhand des Typs – Jetzt: Auswahl der Funktion dynamisch anhand der Instanz Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader 26 28 Konstruktoren, Destruktoren, Zuweisung Methodenzugriff über Zeiger • Instantiierung der Objekte mittels geschachtelter Konstruktorenaufrufe (Basiskonstruktor nach abgeleitetem Konstruktor) • Aufruf der Destruktoren in umgekehrter Reihenfolge • Konstruktoren, Destruktor und Zuweisungsoperator werden nicht vererbt • Wiederholung: Zeiger müssen im Allgemeinen genau von dem Typ sein, auf dessen Objekt sie zeigen – Aber: Konstruktoren und Zuweisungsoperator der abgeleiteten Klasse können die der Basisklasse aufrufen – Sonst: Syntaxfehler • Daher bisher möglich: – Basisklassenzeiger auf Basisklasseninstanz – Zeiger der abgeleiteten Klasse auf entsprechende Instanz • Jetzt neu: Basisklassenzeiger auf Instanz der abgeleiteten Klasse – Objekt wird als ein Objekt der Basisklasse interpretiert! – Es werden auf die Daten des abgeleiteten Objekts die Methoden der Basisklasse angewendet – Methodenaufruf hängt von Zeigertyp und nicht von Objekttyp ab! Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader 7 29 Beispiel: Zeigerzugriff auf abgeleitete Klasse Virtuelle Methoden • Bisher: Zeigertyp bestimmt Methodenaufruf class Point { // ... }; class Circle : public Point { // ... }; Point p; Circle c; • Neu: virtual-Methoden Point* pPtr = &p; Circle* cPtr = &c; pPtr->print(); // print() von Point auf Point-Instanz cPtr->print(); // print() von Circle auf Circle-Instanz pPtr = &c; pPtr->print(); – Instanz (und NICHT Zeigertyp) bestimmt aufgerufene Methode! • Wozu ist das nötig? – Betrachte Klassen Circle, Triangle, Rectangle, alle von Shape abgeleitet // kein Syntaxfehler, da Circle von Point // abgeleitet! // print() von Point auf Circle-Instanz • jede besitzt eigene Methode draw() – um beliebige Form zu zeichnen: • Zeiger der Basisklasse Shape ruft der Reihe nach alle drawMethoden auf • Programm ermittelt richtige Zeichenfunktion zur Laufzeit (dynamisch) • So können alle Formen gleich behandelt werden • Circle-Instanz wird als Point interpretiert! • Auswahl der Methode findet entsprechend anhand des Variablentyps (hier: Zeigertyps) statt • Resultiert aus "Ist ein"-Beziehung zwischen Klassen Programmierkurs C++ – WS 04/05 31 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader 30 32 Zeigerzugriff mit Basisklassenzeiger Virtuelle Methoden in C++ • Es können alle Funktionen der Basisklasse auf dieser Instanz verwendet werden • Zur Verwendung virtueller Methoden in C++ muß Schlüsselwort virtual bei Methode draw angegeben werden • draw muß in jeder abgeleiteten Klasse überladen werden (Methode muß gleiche Signatur besitzten!) – Aufruf genau wie bei Basisklassen-Instanzen – Diese arbeiten auf den Daten der Instanz der abgeleiteten Klasse • Methoden oder Variablen der abgeleiteten Klasse können nicht verwendet werden! – Zugriff darauf liefert Syntaxfehler, da sie nicht in Basisklasse definiert sind • Bis hier hin: Alles aus Vererbung bekannt! – Beispiel: virtual void draw() const; • virtuelle Methoden bleiben in allen abgeleiteten Klassen virtuell – Trotzdem sollte man es wegen der Klarheit in jeder Klasse dazuschreiben • Dynamische Bindung: – wähle korrekte Methode zur Laufzeit – findet ausnahmslos mit Zeigern statt! Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader 8 33 35 Beispiel "Kreis" mit virtueller Funktion (1) Beispiel "Kreis": Testprogramm (1) 1 2 3 4 5 6 8 9 10 11 12 13 14 15 16 17 18 19 20 21 23 24 25 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 // Fig. 10.8: point.h // Point class definition represents an x-y coordinate pair. #ifndef POINT_H #define POINT_H class Point { public: Point( int = 0, int = 0 ); // default constructor void setX( int ); int getX() const; // set x in coordinate pair // return x from coordinate pair void setY( int ); int getY() const; Print als virtual deklariert. set y in coordinate pair Wird in alle abgeleiteten return y from coordinate pair Klassen virtual sein. // // virtual void print() const; // output Point object private: int x; // x part of coordinate pair int y; // y part of coordinate pair }; // end class Point #endif Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader // Fig. 10.10: fig10_10.cpp // Introducing polymorphism, virtual functions and dynamic // binding. #include <iostream> using std::cout; using std::endl; using std::fixed; #include <iomanip> using std::setprecision; #include "point.h" #include "circle.h" // Point class definition // Circle class definition int main() { Point point( 30, 50 ); Point *pointPtr = 0; Circle circle( 120, 89, 2.7 ); Circle *circlePtr = 0; Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader 34 Beispiel "Kreis": Headerdatei (2) 1 // Fig. 10.9: circle.h 2 // Circle class contains x-y coordinate pair and radius. 3 #ifndef CIRCLE_H 4 #define CIRCLE_H 6 #include "point.h" // Point class definition 7 8 class Circle : public Point { 10 public: 11 12 // default constructor 13 Circle( int = 0, int = 0, double = 0.0 ); 14 15 void setRadius( double ); // set radius 16 double getRadius() const; // return radius 17 18 double getDiameter() const; // return diameter 19 double getCircumference() const; // return circumference 20 double getArea() const; // return area 21 22 virtual void print() const; // output Circle object 24 private: 25 double radius; // Circle's radius 27 }; // end class Circle 29 #endif Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader 36 Beispiel "Kreis": Testprogramm (2) 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 // set floating-point numeric formatting cout << fixed << setprecision( 2 ); // output objects point and circle using static binding cout << "Invoking print function on point and circle " << "\nobjects with static binding " << "\n\nPoint: "; point.print(); // static binding cout << "\nCircle: "; circle.print(); // static binding // output objects point and circle using dynamic binding cout << "\n\nInvoking print function on point and circle " << "\nobjects with dynamic binding"; // aim base-class pointer at base-class object and print pointPtr = &point; cout << "\n\nCalling virtual function print with base-class" << "\npointer to base-class object" << "\ninvokes base-class print function:\n"; pointPtr->print(); Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader 9 37 39 Beispiel "Kreis": Testprogramm (3) Zusammenfassung virtuelle Methoden 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 • stellen Polymorphismus zur Verfügung // aim derived-class pointer at derived-class // object and print circlePtr = &circle; cout << "\n\nCalling virtual function print with " << "\nderived-class pointer to derived-class object " << "\ninvokes derived-class print function:\n"; circlePtr->print(); – Gleiche "Mitteilung", hier draw, wird vielen verschiedenen Objekten gegeben • alles durch Basiszeiger – Mitteilung nimmt viele verschiedene Formen an • Basiszeiger auf Basisobjekt, Zeiger von abgeleiteter Klasse auf abgeleitete Klasse: // aim base-class pointer at derived-class object and print pointPtr = &circle; cout << "\n\nCalling virtual function print with base-class" << "\npointer to derived-class object " << "\ninvokes derived-class print function:\n"; pointPtr->print(); // polymorphism: invokes circle's print cout << endl; return 0; } // end main – klar! • Basiszeiger auf abgeleitetes Objekt: – Es können nur Methoden der Basisklasse benutzt werden • Zeiger von abgeleiteter Klasse auf Basisklasse: Zur Laufzeit wird ermittelt, daß pointPtr auf ein Circle Objekt zeigt und die print-Funktion von Circle wird aufgerufen. Dies ist ein Beispiel für Polymorphismus! Programmierkurs C++ – WS 04/05 – Syntaxfehler • Programme sind leichter erweiterbar, da "neue" Klassen mit "alten" gleich behandelt werden Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader 38 40 Beispiel "Kreis": Programmausgabe 6.4 Abstrakte Klassen Invoking print function on point and circle objects with static binding • Abstrakte Klassen – einziger Zweck: eine Basisklasse zu sein! – sind unvollständig Point: [30, 50] Circle: Center = [120, 89]; Radius = 2.70 • abgeleitete Klassen füllen Lücken Invoking print function on point and circle objects with dynamic binding – Es können keine Objekte von diesen Klassen erzeugt werden – ABER: Es können Zeiger und Referenzen von diesem Typ benutzt werden! Calling virtual function print with base-class pointer to base-class object invokes base-class print function: [30, 50] • Konkrete Klassen Calling virtual function print with derived-class pointer to derived-class object invokes derived-class print function: Center = [120, 89]; Radius = 2.70 – Objekte können davon erzeugt werden – implementieren alle Funktionen, die deklariert werden Calling virtual function print with base-class pointer to derived-class object invokes derived-class print function: Center = [120, 89]; Radius = 2.70 Programmierkurs C++ – WS 04/05 • Abstrakte Klassen sind nicht notwendig, aber nützlich Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader 10 41 43 Abstrakte Klassen in C++ 6.5 Virtuelle Destruktoren • Benötigen mindestens eine rein virtuelle Methode – Deklaration rein virtueller Methoden: virtual void draw() const = = 0; 0; • Problem: Was passiert, wenn ein Objekt mit delete zerstört wird, auf das über einen Basiszeiger zugegriffen wird? – besitzen Implementationen – können überladen werden • Einfache Lösung: • Reguläre (=nicht reine) virtuelle Methoden – deklariere Destruktor als virtuell! – Dadurch werden alle Destruktoren von abgeleiteten Klassen virtuell und es wird immer der richtige ausgewählt – Regel: sobald mindestens eine Methode virtuell ist, sollte auch ein virtueller (evtl. leerer) Destruktor erzeugt werden! • Rein virtuelle Methoden – besitzen keine Implementation – müssen überladen werden! • Abstrakte Klassen können auch Daten und konkrete Methoden besitzen (eine Methode muß jedoch rein virtuell sein) • Zeiger auf abstrakte Basisklassen • Konstruktoren können nicht virtuell sein. Wieso nicht? – sinnvoll für Polymorphismus Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader 42 Beispiel für abstrakte Basisklasse • Abstrakte Klasse Shape – Definiert draw als rein virtuelle Methode • Circle, Triangle, Rectangle sind von Shape als konkrete Klassen abgeleitet: – Jede muß Methode draw überladen • Bildschirmmanager weiß, daß jedes Objekt sich selbst zeichnen kann Programmierkurs C++ – WS 04/05 Christian Hagemeier – Lehrstuhl Prof. Dr. Schrader 11