3. Objektorientierung Ergänzungen
3.1 Strukturen Anstelle des Schlüsselwortes class kann auch das Wort struct verwendet werden. Unterschied zu class: Voreinstellung für die Elemente ist public (bei class ist es private) Strukturen werden üblicherweise eingesetzt, wenn man nur einen zusammengesetzten Datentyp ohne zugehörige Methoden verwenden will. Man kann dann auf alle Eigenschaften immer direkt zugreifen. Beispiel structartikel // Definition unsigned int artikelnummer; string bezeichnung; unsigned int bestand; double stueckpreis; }; Artikel a, b=118, Sonderposten,5,12.99};// initialisiert cin>> a.artikelnummer>>a.bezeichnung; if ( a.bestand< limit ) bestelle(&a); Man stellt aber schnell fest, dass es in der Regel ohne "Funktionen" schlecht geht. Dr. Norbert Spangler / Grundlagen der Informatik 23.04.2016 2
class Kreis // Klassendefinition private: Punkt mittelpunkt; double radius, umfang, flaeche, durchmesser; static const double pi; public:. }; 3.2 Klassenspezifische Elemente Durch die Verwendung des Schlüsselworts static wird eine Eigenschaft zur klassenspezifischen Eigenschaft und ist nur einmal innerhalb der Klasse vorhanden. Sie erhält ihren Wert außerhalb der Klasse, da sie unabhängig von Objekten immer existiert. Problem: Wie erhält pi seinen Wert? const double Kreis::pi=3.1415926535897932384626433832795028841972 ; void main..} Dr. Norbert Spangler / Grundlagen der Informatik 23.04.2016 3
Klassenspezifische Elemente Weitere Beispiele für klassenspezifische Daten: Zähler für die Anzahl der deklarierten Objekte einerklasse geändert mittels Konstruktor/Destruktor Umrechnungsfaktoren wie Wechsel-/Devisenkurse Geschwindigkeit bei Schiffen km/h Knoten. Weiterhin können auch klassenspezifische Methoden vorkommen. class XY private: static anzahlobjekte; public: static getanzahl(); }; Sie können auch über den Klassennamen aufgerufen werden (unabhängig von einem Objekt) und arbeiten nur mit den statischen Daten: cout<<"anzahl Objekte"<<XY::getAnzahl()<<endl; Dr. Norbert Spangler / Grundlagen der Informatik 23.04.2016 4
3.3 Der this-zeiger Manchmal ist es in einer Methode einer Klasse erforderlich, das gesamte Objekt zu kennen, für welches die Methode aufgerufen wurde. Beispiel: Die Methode reserviere der Klasse Artikel soll eine Bestellung auslösen, wenn der Bestand einen speziellen Wert interschreitet mittels einer Funktion void bestellen(artikel*) des Bestellsystems. if ( bestand <=. ) bestellen(?????); Man benötigt hierzu de n Namen oder die Adresse des aktuellen Objekts der Klasse Artikel, worüber reserviere aufgerufen wurde. Dr. Norbert Spangler / Grundlagen der Informatik 17.04.2016 5
Der this-zeiger Problem: Wie erkennt die Methode, von welchem Objekt sie aufgerufen wurde und welche Eigenschaften zu nehmen sind? Lösung: Jede Methode hat eine "versteckte Zeigervariable". In dieser ist die Adresse des gewählten Objektsgespeichert. Dies ist der this-zeiger. Dieser kann nicht geändert werden. Die Deklaration kann man sich so vorstellen: Klasse * const this=&aktuelles Objekt. this ist ein Schlüsselwort Dr. Norbert Spangler / Grundlagen der Informatik 17.04.2016 6
Lösung mit dem this-zeiger Der this-zeiger unsigned int Artikel::reserviere(unsigned int menge) void bestellen(artikel*); unsigned int abgang; if ( menge<=bestand) abgang=menge; else abgang=bestand; bestand-=abgang; if ( bestand <=.. ) bestellen(this); return abgang; }; Dr. Norbert Spangler / Grundlagen der Informatik 17.04.2016 7
Verwendung des this-zeigers Programmierstil: Obwohl in einer Methode alle Eigenschaften des Objekts einer Klasse direkt über den Eigenschaftsnamen angesprochen werden können, kann man dies auch über den this-zeiger abwickeln: Beispiel: In der Klasse Artikel kann in einer Methode entwederdirekt bestand oder über den this-zeiger this->bestand verwendet werden. Vorteil: leichte Unterscheidung zwischen lokalen Variablen und Eigenschaften der Klasse. Dr. Norbert Spangler / Grundlagen der Informatik 17.04.2016 8
Verwendung des this-zeigers unsigned int Artikel::reserviere(unsigned int menge) void bestellen(artikel*); unsigned int abgang; if ( menge<= this->bestand ) abgang=menge; else abgang= this->bestand; this->bestand-=abgang; if ( this->bestand <= 100 ) bestellen(this); return abgang; }; Eigenschaft Lokale Variable Dr. Norbert Spangler / Grundlagen der Informatik 17.04.2016 9
3.4 Das friend-konzept Bisher war es nur mittels "protected" möglich, einer anderen Klasse (und da auch nur abgeleiteten) den Zugriff auf private-elemente zu gewähren. Wenn spezielle Funktionen oder andere Klassen besonders "eng" mit einer Klasse X "zusammenarbeiten" kann es sinnvoll sein, diesen den Zugriff auf die private-elemente zu genehmigen. Dies geschieht mittels einer friend-deklaration in der Klasse X, d.h. in der Klasse, welche "Eigentümer" der Daten ist. Die "Eigentümer-Klasse" vergibt die Zugriffsrechte an andere. class X }; public: friend class Y;//alle Methoden der Klasse Y dürfen auf die //private-elemente von X zugreifen friend void f1( );// ebenso die Funktion f1 Dr. Norbert Spangler / Grundlagen der Informatik 23.04.2016 1 0
3.5 Typumwandlungen für Klassen Implizite Typumwandlungen gibt es bei arithmetischen Operanden z.b. int <-> double Explizite Typumwandlungen erfolgen durch Anwendungeines Cast-Operators Bei Klassen besteht die Aufgabe darin, aus einem Zeiger auf die abgeleiteten Klasse einen Basisklassenzeiger zu machen oder umgekehrt, sowie aus einem Objekt der abgeleiteten Klasse eines der Basisklasse zu machen oder umgekehrt (selten) Dr. Norbert Spangler / Grundlagen der Informatik 23.04.2016 11
Typumwandlungen Up-Cast jeder Zeiger auf eine abgeleitete Klasse kann automatisch in einen Basisklassenzeiger konvertiert werden. Dies gilt auch für Objekte. Zeiger auf Druckerpapier kann zum Zeiger auf Artikelwerden Druckerpapier ist automatisch vom TypArtikel Beispiel: Druckerpapier kk,*ptrk=&kk; ptrk->anzeigen();//aufruf der Anzeige-Methode von Druckerpapier static_cast<artikel*>(ptrk) ->anzeigen(); //jetzt vonartikel Der static_cast-operator ist generell dafür gedacht, implizit mögliche Typumwandlungen umzusetzen. Dr. Norbert Spangler / Grundlagen der Informatik 23.04.2016 12
Weitere Cast-Operatoren Down-Cast: Basisklasse -> abgeleitete Klasse ist riskant Artikel * pa; Druckerpapier *pd=static_cast<druckerpapier>(pa); Riskant:Nur sinnvoll wenn in pa die Adresse einesobjekts vom Typ Druckerpapiert ist Dynamic-Cast: Down-Cast mit Plausibilitätskontrolle (für polymorpheklassen) Druckerpapier *pd=dynamic_cast<druckerpapier>(pa); if ( pd==null) // kein Druckerpapier else //ist Druckerpapier Sonstige Cast-Operatoren Const-Cast: macht const rückgängig Reinterpret-Cast: Bitweise neue Interpretation ist selten Dr. Norbert Spangler / Grundlagen der Informatik 23.04.2016 13
3.6 Zeiger und Vererbung/Polymorphie Bei der Wahl einer über eine Zeigervariable aufgerufenen Methode ist für die Auswahl der Typ des Zeigers entscheidend. Analog Praktikum: Es wird die Methode der Basisklasse Artikel aufgerufen. Wünschenswert wären aber die passenden(!) Methoden der abgeleiteten Klassen void main() vector<artikel*> v; Toner t(110,"laserjet"); Druckerpapier d (120,"Laserjet,5); v[0]=&t; //dies geht v[1]=&d; } v[0]->anzeigen(); v[1]->anzeigen(); Dr. Norbert Spangler / Grundlagen der Informatik 23.04.2016 14
Zeiger und Vererbung/Ziel void main() int i; vector<artikel*> v; Toner t(110,"laserjet"); Druckerpapier d (120,"Laserjet,5); v[1]=&d; static_cast<druckerpapier*>(v[1]) ->anzeigen(); } cin>>i; static_cast<???????r*>(v[i]) ->anzeigen(); Eine Lösung wäre ein Down-Cast auf die abgeleitete Klasse. Es ist aber in der Regel nicht bekannt, auf welchen Objekttyp der Basisklassenzeiger v[i] zeigt. Dr. Norbert Spangler / Grundlagen der Informatik 23.04.2016 15
Zeiger und Vererbung Konventionelle Lösung: Abfrage des Typs der abgeleiteten Klasse Voraussetzung beispielsweise: Einführung einer Eigenschaft Typ und einer Methode gettyp in der Basisklasse if ( v[i]->gettyp() == 'D' ) //fuer Druckerpapier static_cast<druckerpapier*>(v[1]) ->anzeigen(); else if. -> umständlich/fehleranfällig viele Anpassungen bei neuen abgeleiteten Klassen Dr. Norbert Spangler / Grundlagen der Informatik 23.04.2016 16
Objektorientierte Lösung: Polymorphie Unter Polymorphie versteht man Wahl der passenden Methode, wenn der Typ (die Klasse) eines Objektes, dessen Adresse in einem Zeiger gespeichert ist, erst zur Laufzeit feststeht. Es kann also erst zur Laufzeit entschieden werden, welche Methoden (z. B. zur Anzeige) zu nehmensind. Dies ist beispielsweise dann der Fall, wenn der Anwender ein Objekt auswählen kann. Hierzu werden virtuelle Methoden implementiert, bei deren Ausführung dann der Compiler dafür sorgt, dass zur Laufzeit die passende Version ausgeführt wird, wenn das Objekt über einen Zeiger (auf die Basisklasse) angesprochen wird. classartikel Deklaration : Das Schlüsselwort virtual wird virtual void anzeigen(); in der Deklaration (Headerdatei) der Basisklasse Vorangestellt. Dies gilt dann automatisch für alle }; abgeleiteten Klassen. Dr. Norbert Spangler / Grundlagen der Informatik 23.04.2016 17
Virtuelle Methoden Jede Klasse mit virtuellen Methoden erhält eine Virtuelle Methodentabelle ( VMT) Diese enthält die Adressen aller virtuellen Methoden. Jedes Objekt erhält einen (internen) Zeiger auf die Methodentabelle der Klasse, zu welcher dieses Objekt gehört. Die Wahl der Methode erfolgt über diesen Zeiger, der zum Objekt gehört. Damit ist bei Zugriff über Zeiger der Typ des Objekts für die Auswahl der Methode verantwortlich und nicht mehr, wie bisher, der Typ des Zeigers. Nachteile: Erhöhter Speicherplatz- und Zeitbedarf Vorteil: Elegante Lösungen möglich, welche sonst aufwändiger wären Dr. Norbert Spangler / Grundlagen der Informatik 23.04.2016 18
3.7 Abstrakte Klassen Prinzipiell gibt es die Klassen Artikel bzw. Bauteil nicht real. Es macht daher in der Regel keinen Sinn, hiervon Objekte zu deklarieren. Dies kann durch den Compiler unterstützt werden, wenn man rein virtuelle Funktionen in der Klassendefinition hat. Diese erhält man durch das Schlüsselwort virtual, ergänzt durch die Endung =0; virtual typ name( )=0; // rein virtuelle Funktion- nicht implementiert Dann kann von der dazugehörigen Klasse kein Objekt mehr deklariert werden. Abstrakte Klassen dienen u.a. als Basis einer Vererbungshierarchie. Beispiel: virtual void display()=0; Dr. Norbert Spangler / Grundlagen der Informatik 23.04.2016 19
3.8 Überladen von Operatoren Ein Operator ist überladen, wenn er für unterschiedliche Datentypen definiert ist. Beispiele: + < == usw. Man kann Operatoren für weitere Datentypen (i.a. Klassen) zulassen oder auch als globale Funktion definieren Bisherige Beispiele: Klasse Bruch KlasseArtikel Operatoren + - * / Operator < : Artikelnummer oder Bezeichnung? Man kann nicht: neue Operatoren erfinden bereits existierende Bedeutungen verändern ( + für int ist festgelegt!!!) unäre in binäre ändern oder umgekehrt den Vorrang ändern Dr. Norbert Spangler / Grundlagen der Informatik 23.04.2016 20
Übersicht Rechenoperatoren Zusammengesetzte Rechenoperationen Vergleiche Ein-/Ausgabe = Zuweisungsoperator / ebenso Kopierkonstruktor [] Indexoperator ( ) Funktionsaufruf -> später in Programmieren 3 Dr. Norbert Spangler / Grundlagen der Informatik 23.04.2016 21
class Bruch //siehe WS Kap 13 }; Überladen von Operatoren: Ein Beispiel Bruch Bruch::addiere(Bruch b) Bruch h; h.zaehler=zaehler*b.nenner+b.zaehler*nenner; h.nenner=nenner*b.nenner; h.kuerzen(); return h; } void main Bruch a,b,c;. c=a.addiere(b); }
Operatorfunktion: Rechenoperationen/Vergleiche Schöner (lesbarer) wäre bei Brüchen die Schreibweise c=a+b; Dies erreicht einfach man durch die Verwendung von Operatorfunktionen, indem man der Funktion einen besonderen Namen gibt operator operationszeichen also hier operator + Bruch Bruch::operator+(Bruch b) Bruch h; h.zaehler=zaehler*b.nenner+b.zaehler*nenner; h.nenner=nenner*b.nenner; h.kuerzen(); return h; } void main Bruch a,b,c,d;. c=a+b; d=c.operator+(b);//geht auch } Analog +=, < siehe Programmierung I/Kap 13 Dr. Norbert Spangler / Grundlagen der Informatik 23.04.2016 24
Operatorfunktion: anderer Datentyp Eine Operatorfunktion kann auch mit unterschiedlichen Typen arbeiten. Gehört sie zu einer Klasse ist der erste (linke) Operand immer vom Typ der Klasse und ist das Objekt, mit dem die Methode (=Operatorfunktion) aufgerufen wird. Der zweite(rechte) operand ist dann der Parameter der Methode.. Beispiel: Addition eines Bruchs zu einer ganzen Zahl Bruch Bruch::operator +(int x) Bruch h; h.zaehler=zaehler+x*nenner; h.nenner=nenner; h.kuerzen(); return h } void main Punkt a,b;. c=a+2;//aufruf } Dr. Norbert Spangler / Grundlagen der Informatik 23.04.2016 25
Operatorfunktionen: cast Umwandeln eines Bruchs in double(bisher): double h=p.umwandeln(); Bessere Alternative: double h=static_cast<double>(p); Erforderliche Methode: class Bruch // Klassendefinition double() const; }; Bruch::operator double() const return double(zaehler)/double(nenner); } Anmerkung: - durch die Angabe const wird das Objekt nichtgeändert - Methode hat keinen Typ, da dieser durch die Angabe double feststeht - Allgemein: Typ() const; Dr. Norbert Spangler / Grundlagen der Informatik 23.04.2016 26
Ein-/Ausgabe Wiederholung: cin>>a ist einausdruck, dessen Wert bei korrekt abgelaufener Eingabe "cin" ist und ansonsten 0 cin ist ein Objekt der Eingabeklasse istream und analog ist cout ein Objekt von ostream. Damit haben Operatorfunktionen zur Ein-/Ausgabe folgende Form: istream& operator >> (istream&, Typ&) ostream& operator<< (ostream&, Typ&) Der erste Parameter ist immer links, der zweite rechts vom Operatorsymbol. Anmerkung: üblicherweise werden Referenzen verwendet. Zu welcher Klasse gehört nun diese Operatorfunktion? Formal wäre es das erste (linke) Argument -> also istream, wasnicht möglich ist, da diese Klasse vorgegeben ist. Lösung: globale Funktion (wie anfangs erwähnt). Dr. Norbert Spangler / Grundlagen der Informatik 23.04.2016 26
Globale Operatorfunktion zur Ein-/Ausgabe istream& operator>>(istream &is, Bruch &q) int z,n; is>>z>>n; Bruch h(z,n); q=h; return is; } ostream& operator<<(ostream &os, Bruch &q) os<<q.zaehler<<"/"<<q.nenner <<" "; return os; } Voraussetzung Die beiden Operatorfunktionen sind als friend in der Klasse Punkt deklariert. Ansonsten müsste mit get/set gearbeitet werden Dr. Norbert Spangler / Grundlagen der Informatik 23.04.2016 27
Beispielprogramm #include Bruch.h" #include <istream> #include <ostream> #include <iostream> using namespace std; void main() istream& operator>> (istream&, Bruch&); ostream& operator<< (ostream&,bruch&); Bruch a,b,c; cout<<" 2 Brueche eingeben\n"; cin>>a>>b; //Aufruf Eingabe c=a+b; //Aufruf Rechnen cout<<"summe\n"; cout<<c; //AufrufAusgabe } Dr. Norbert Spangler / Grundlagen der Informatik 23.04.2016 28
Sonstige interessante Operatorfunktionen Indexoperator für Container C Ziel: Schreibweise x[i] wenn kein Index existiert Wenn eine Klasse Zeiger als Eigenschaften enthält sind beispielsweise folgende einfache Operatoren interessant: == bzw.!= Operator für eine Klasse K Ziel: if ( x == y ) Kopier- und Zuweisungsoperator: Ziel: x=y; Dr. Norbert Spangler / Grundlagen der Informatik 23.04.2016 30
3.9 Zusammenfassung Objektorientierung Die 4 Grundprinzipien der Objektorientierung Datenabstraktion Datenkapselung Vererbung Polymorphie Dr. Norbert Spangler / Grundlagen der Informatik 23.04.2016 30
Datenabstraktion Klassen werden gebildet durch die Beschreibung ihrer Eigenschaften und Methoden Eine Klasse ist also charakterisiert durch die Zusammenfassung aller Gemeinsamkeiten von Eigenschaften und Methoden. Eine Klasse ist das Muster (die Schablone) für Objekte. Objekte sind Variable (Instanzen) vom Typ der zugehörigen Klasse. Datenkapselung Es gibt eine Einteilung der Eigenschaften/Methoden in 2 Gruppen : öffentlich (public) -> in den aufrufenden Programmen bekannt/sichtbar nicht-öffentlich (private) -> nur innerhalb der Klasse bekannt/sichtbar Üblicherweise sind Eigenschaften private und Methoden public. Damit erfolgt ein kontrollierter Zugriff auf die private-eigenschaften (Daten) der Objekte durch Bereitstellung zugehöriger public-methoden. Dr. Norbert Spangler / Grundlagen der Informatik 23.04.2016 31
Vererbung Aus existierenden (Basis) Klassen könne abgeleitete Klassen erzeugt werden, welche die Eigenschaften und Methoden dieser Basisklasse erben : Eigene Eigenschaften können die Eigenschaften der Basisklasse ergänzen. Bsp.: Druckerpapier hat die zusätzliche Eigenschaft packungsgroesse Eigene Methoden können die Methoden aus den Basisklassen ergänzen oder auch überschreiben (=ersetzen). Polymorphie Darunter versteht man Fähigkeit, erst zur Laufzeit des Programmes für das über eine Zeigervariable adressierte Objekt die passende Methode auszuwählen. Eine derartig ausgewählte Methode heißt auch virtuelle Methode. Andere Begriffe: dynamisches oder spätes Binden Dr. Norbert Spangler / Grundlagen der Informatik 23.04.2016 32