Programmieren 2 C++ Überblick 1. Einführung und Überblick 2. Klassen und Objekte: Datenkapselung 3. Erzeugung und Vernichtung von Objekten 4. Ad-hoc Polymorphismus 5. Behälter und Iteratoren 6. Templates und generische Programmierung 7. Standard Template Library 8. Vererbung 10. Fehlerbehandlung 11. Namensräume und Typinformation zur Laufzeit 12. Dateien und Ströme 13. Zusammenfassung objektorientierter Merkmale Prof. Dr. Björn Dreher Programmieren 2 C++ 555 Programmieren 2 C++ Überblick: 9.1 Einleitung Prof. Dr. Björn Dreher Programmieren 2 C++ 556
9.1 Einleitung Objektorientierte Anwendung: Menge von Objekten, die zueinander in Beziehung stehen Diese Beziehungen können unterschiedlichster Natur sein: Vererbung, d.h. Spezialisierung (is-a Beziehung) Aggregation (Teil-Ganzes Beziehung) Assoziation Delegation ( Benutzt -Beziehung) Betrachten wir die einzelnen Fälle und ihre Realisierung in C++ Prof. Dr. Björn Dreher Programmieren 2 C++ 557 Programmieren 2 C++ Überblick: 9.1 Einleitung Prof. Dr. Björn Dreher Programmieren 2 C++ 558
Vererbung wurde bereits ausführlich besprochen Hier: Besondere Grenzfälle Wichtig bei public Vererbung: Die Beziehung zwischen den Klassen muss auch semantisch wirklich eine is-a Beziehung sein Eine abgeleitete Klasse kann als Subtyp der Oberklasse angesehen werden. Ein Objekt einer abgeleiteten Klasse kann immer an die Stelle eines Objektes der Oberklasse treten (alle Methoden und Attribute der Oberklasse sind vorhanden) Prof. Dr. Björn Dreher Programmieren 2 C++ 559 Nachdenkenswerte Fälle Ein Quadrat ist mathematisch ein Rechteck (wobei beide Seitenlängen gleich sind), also ein Spezialfall eines Rechtecks wie ein Kreis ein Spezialfall einer Ellipse ist Graphische Repräsentation dieses Beispiels: hoehe: int breite: int Rechteck hoeheaendern() breiteaendern() Quadrat hoeheaendern() breiteaendern() Prof. Dr. Björn Dreher Programmieren 2 C++ 560
Die Spezialisierung von Rechteck zu Quadrat drückt sich in der folgenden öffentlichen Vererbung aus: class Quadrat : public Rechteck ; Eine Klasse Rechteck wird wohl Methoden haben, die es erlauben, die Seiten unabhängig voneinander zu verändern: class Rechteck virtual void hoeheaendern(int neu) hoehe = neu; virtual void breiteaendern(int neu) breite = neu; private: int hoehe; int breite; ; Diese Methoden haben in der Klasse Quadrat nichts zu suchen Prof. Dr. Björn Dreher Programmieren 2 C++ 561 Rechteck -> Quadrat Lösung aus Literatur: Vererbung bei einer Spezialisierung auch als Einschränkung (constraint) der Oberklasse benutzen (Booch: inheritance for restriction ) Wichtige Eigenschaft der objektorientierten Vorgehensweise: Begriffe aus dem Anwendungssprachraum werden über Analyse und Design weitgehend bis in die Implementierung übertragen Mathematisch ist ein Quadrat ein Rechteck Aber ist es sinnvoll bei der Implementierung dann auch für das Quadrat zwei Seitenlängen mitzuschleppen? Zusätzlich muss man dafür Sorge tragen, dass diese immer den gleichen Wert besitzen Prof. Dr. Björn Dreher Programmieren 2 C++ 562
Rechteck -> Quadrat Vertrag zwischen Klasse und Benutzer ( programming by contract ) Klasse muss diesen einhalten, also dafür sorgen, dass ein Quadrat auch immer ein Quadrat bleibt Dies lässt sich natürlich relativ leicht bewerkstelligen: class Quadrat : public Rechteck virtual void hoeheaendern(int neu) Rechteck::hoeheAendern(neu); Rechteck::breiteAendern(neu); virtual void breiteaendern(int neu) hoeheaendern(neu); ; Damit wird der mit Quadrat geschlossene Vertrag eingehalten, auch für alle möglichen Nachfahren. Prof. Dr. Björn Dreher Programmieren 2 C++ 563 Rechteck -> Quadrat Alternative: Rechteckklasse wird benutzt, die Quadrat-Klasse delegiert an die Rechteck-Klasse: class Quadrat // Keine Vererbung Quadrat(const Ort& o, int seite) : r(o, seite, seite) // privates Rechteck // initialisieren virtual void seiteaendern(int neu) r.hoeheaendern(neu); r.breiteaendern(neu); // Weitere Methoden, die Methoden der Klasse // Rechteck benutzen private: Rechteck r; ; Prof. Dr. Björn Dreher Programmieren 2 C++ 564
Rechteck -> Quadrat Graphische Darstellung: r: Rechteck Quadrat Quadrat() seiteaendern() Komposition hoehe: int breite: int Rechteck hoeheaendern() breiteaendern() Prof. Dr. Björn Dreher Programmieren 2 C++ 565 Rechteck -> Quadrat: Ergebnis: Der Benutzer von Quadrat sieht die Methoden der Klasse Rechteck nicht mehr Das eingebettete Rechteck ist voll gekapselt Dennoch wird der von der Klasse Rechteck zur Verfügung gestellte Code wieder verwendet, indem Aufgaben an die Klasse Rechteck delegiert werden Nachteil: Quadrat und Rechteck können nicht polymorph verwendet werden Ließe man Quadrat und Rechteck von einer gemeinsamen abstrakte Klasse GraphObject erben, hätte man das Problem, dass der dort definierte Mittelpunkt sowohl an Quadrat als auch an das benutzte Rechteck vererbt würde -> auch nicht schön! Prof. Dr. Björn Dreher Programmieren 2 C++ 566
Rechteck -> Quadrat Doch die bessere Idee (?) Beide Klassen unabhängig voneinander von GraphObject ableiten und in Kauf nehmen, dass ein Teil des Codes doppelt geschrieben werden muss, insbesondere wenn Quadrat nur wenig Code von Rechteck verwendet Prof. Dr. Björn Dreher Programmieren 2 C++ 567 Ähnliches Problem: Stack und Liste Ist Stack eine Spezialisierung einer Liste? Hier ist Delegation sicher die bessere Lösung, da beide Listen höchst wahrscheinlich nicht als polymorphe Objekte behandelt werden müssen Ein Objekt einer abgeleiteten Klasse muss jederzeit an die Stelle eines Basisklassenobjektes treten können, um public Vererbung zu rechtfertigen Nur dann kann die abgeleitet Klasse als Subtyp der Basisklasse angesehen werden Quadrat und Rechteck: Rechteck habe eine Methode SeitenverhaeltnisAendern(). Auswirkung auf Quadrat? Prof. Dr. Björn Dreher Programmieren 2 C++ 568
Programmieren 2 C++ Überblick: 9.1 Einleitung Prof. Dr. Björn Dreher Programmieren 2 C++ 569 Teil-Ganzes Beziehung (engl. part-of) Ein Objekt besteht aus mehreren anderen Objekten Die Teile sind in dem ganzen Objekt enthalten Dies kann in Form eingebetteter Objektinstanzen (strengere Form der Aggregation: Composition) oder in Form von Zeigern auf die Teilobjekte (Teilobjekte könne evtl. auch unabhängig vom Ganzen existieren) implementiert werden. Prof. Dr. Björn Dreher Programmieren 2 C++ 570
Eingebettet: Komposition Das Ganze sei eine Tastatur. Sie besteht aus einem Gehäuse, einem Kabel, einer Platine und 102 Tasten: class Tastatur private: Gehaeuse eingehaeuse; Kabel einkabel; Platine eineplatine; Taste tastenfeld[102]; ; Tastatur 102 Kabel Gehäuse Platine Taste Prof. Dr. Björn Dreher Programmieren 2 C++ 571 Programmieren 2 C++ Überblick: 9.1 Einleitung Prof. Dr. Björn Dreher Programmieren 2 C++ 572
Allgemeinste Art der Beziehung zwischen zwei Objekten Aggregation kann als ein Spezialfall der Assoziation angesehen werden. Bestehende Assoziation heißt, dass die beteiligten Objekte sich kennen, d.h. sie haben i.a. als eines ihrer Attribute einen Zeiger oder eine Referenz auf das andere Objekt Dies kann in beide Richtungen gehen oder auch nur in eine, je nach Notwendigkeit des Navigierens von einem Objekt zum anderen Prof. Dr. Björn Dreher Programmieren 2 C++ 573 Beispiel Person Firma bekanntenkreis * name: String arbeitgeber: Firma* bekanntenkreis list<person*> Person() lerntkennen(person&) wieheisstdu(): String findetarbeitbei(firma&) kuendigt() ist beschäftigt bei * mitarbeiter: list<person*> Firma() einstellen(person*) Prof. Dr. Björn Dreher Programmieren 2 C++ 574
class Person; // Vorwärtsdeklaration class Firma einstellen(person* p); private: list<person*> mitarbeiter; ; bekanntenkreis * Person name: String arbeitgeber: Firma* bekanntenkreis list<person*> Person() lerntkennen(person&) wieheisstdu(): String findetarbeitbei(firma&) kuendigt() ist beschäftigt bei * Firma mitarbeiter: list<person*> Firma() einstellen(person*) Prof. Dr. Björn Dreher Programmieren 2 C++ 575 class Person void lerntkennen(person& p) bekanntenkreis.hinzufuegen(&p); String wieheisstdu() return name; void findetarbeitbei(firma& f) arbeitgeber = &f; f.einstellen(this); void kuendigt() arbeitgeber->entlassen(this); arbeitgeber = NULL; private: String name; Firma *arbeitgeber; list<person*> bekanntenkreis; bekanntenkreis * Person name: String arbeitgeber: Firma* bekanntenkreis list<person*> Person() lerntkennen(person&) wieheisstdu(): String findetarbeitbei(firma&) kuendigt() ist beschäftigt bei * Firma mitarbeiter: list<person*> Firma() einstellen(person*) Prof. Dr. Björn Dreher Programmieren 2 C++ 576
Ein mögliches Hauptprogramm: Firma is_bremen_gmbh; Person walter, thomas, sonja; thomas.findetarbeitbei(is_bremen_gmbh); walther.findetarbeitbei(is_bremen_gmbh); thomas.lerntkennen(sonja); thomas.lerntkennen(walter); thomas.kuendigt(); Prof. Dr. Björn Dreher Programmieren 2 C++ 577 Programmieren 2 C++ Überblick: 9.1 Einleitung Prof. Dr. Björn Dreher Programmieren 2 C++ 578
Benutzt -Beziehung Ein Objekt A macht sich die Dienste eines anderen Objektes B nutzbar Es delegiert Aufgaben an dieses andere Objekt Dazu besitzt A i.a. einen Zeiger auf B als Attribut Damit kennt es Objekt B und kann dessen öffentliche Methoden benutzen Prof. Dr. Björn Dreher Programmieren 2 C++ 579 Beispiel Ein Heizregler benutzt einen Temperaturfühler zum Messen der Temperatur: class Heizregler // Konstruktoren bool warmgenug() return (dasthermometer->wiewarm() > 20); private: Temperaturfuehler* dasthermometer; ; Auch diese Beziehung ist eine Assoziation Prof. Dr. Björn Dreher Programmieren 2 C++ 580