Programmieren II Klassen Programmieren II Klassen Ergänzende Bemerkungen Ergänzende Bemerkungen Konstruktoren Ziel dieses Abschnitts • keine Zusammenfassung, aber • • Wiederholung von einigen relevanten Aspekten (/* Aufgreifen von Fragen/Problemen beim Lösen der Praktikumsaufgaben */) Diskussion eines Aspekts, den wir bisher „fast“ vollständig ausgeklammert haben • jede Klasse hat einen Default-Konstruktor • wird aufgerufen, wenn ein Objekt von diesem Typ (/* eine Instanz dieser Klasse */) erzeugt wird sobald ein eigener Konstruktor deklariert wird, steht dieser Default-Konstruktor nicht mehr zur Verfügung • manchmal ist es sinnvoll, diesen Default-Konstruktor wieder verfügbar zu machen 9/1 Programmieren II Klassen 9/2 Programmieren II Klassen Ergänzende Bemerkungen Ergänzende Bemerkungen Beispiel class Bruch { private: int zaehler_; int nenner_; public: Bruch(int, int); void display(); }; Beispiel ... Bruch bruch1; diese Anweisung erzeugt eine Fehlermeldung !!! ... besser ... int x = 7; int y = -21; Bruch br1(3,3); Bruch br2(x,y); class Bruch { private: int zaehler_; int nenner_; public: Bruch( ) { }; Bruch(int, int); void display(); }; Default-Konstrukor re-definieren einzige Möglichkeit, ein Objekt vom Typ Bruch zu erzeugen 9/3 Hinweis: hierbei handelt es sich um ein sehr einfache Variante einer so genannten inline-Funktion 9/4 Programmieren II Klassen Programmieren II Klassen Ergänzende Bemerkungen Ergänzende Bemerkungen Destruktoren • • Beispiel jede Klasse hat einen Default-Destruktor • wird automatisch (/* !!! */) aufgerufen, wenn ein Objekt von diesem Typ zerstört wird (/* etwa beim Verlassen des Blocks, in dem das Objekt erzeugt wurde bzw. beim Beenden des Programms */) manchmal ist es sinnvoll, diesen Default-Destruktor zu redefinieren • Verwendung von Klassen-Variablen (/* in der Klassendeklaration mit dem Schlüsselwort static versehen */) • Verwendung von dynamischen Datenelementen selbstdeklarierter Destruktor class Konto { private: const string inhaber_; int nummer_; double stand_; static double zinssatz_; static int anzahl_; public: Konto(string, int, double); void display( ); void einzahlen(double); void abschluss(); static int get_anzahl(); static void set_zinssatz(double); ~Konto( ); }; 9/5 9/6 Hinweis: einen Destruktor sollte man nicht „selbst“ aufrufen Programmieren II Klassen Programmieren II Klassen Ergänzende Bemerkungen Beispiel Ergänzende Bemerkungen Konto::Konto (string s, int n, double a) : inhaber_(s) { nummer_ = n; stand_ = a; ++anzahl_; } Beispiel class Konto { private: ... static double zinssatz_; static int anzahl_; public: ... }; Konto::~Konto ( ) { --anzahl_; } Hinweis: Klassenvariablen müssen (/* außerhalb der Klassendeklaration */) separat definiert und initialisiert werden (/* sie spielen die Rolle von globalen Variablen und sollen auch dann verfügbar sein, wenn noch kein Objekt der Klasse existiert */) ... stellt sicher, daß der Wert der Klassenvariable anzahl_ die Anzahl der aktuell verfügbaren Objekte vom Typ Konto korrekt widerspiegelt 9/7 double Konto::zinssatz_ = 3.3; int Konto::anzahl_ = 0; 9/8 Programmieren II Klassen Programmieren II Klassen Ergänzende Bemerkungen Ergänzende Bemerkungen Beispiel‘ selbstdeklarierter Destruktor Beispiel‘ class Polynom { private: Term * first_; public: Polynom( ); ~Polynom( ); void display( ); void add_term( int, int ); double evaluate( double ); }; Polynom::~Polynom( ) { Term * ptr_term = first_; Term * help = NULL; while ( ptr_term != NULL ) { help = (*ptr_term).get_next(); delete ptr_term; ptr_term = help; } } 9/9 Programmieren II Klassen 9/10 Programmieren II Klassen Ergänzende Bemerkungen Ergänzende Bemerkungen Kopier-Konstruktoren • • Beispiel jede Klasse hat einen Default-Kopier-Konstruktor • wird automatisch (/* !!! */) aufgerufen, wenn ein Objekt kopiert werden soll (/* etwa explizit durch eine entsprechende Zuweisung bzw. bei der Abarbeitung einer Methode */) manchmal ist es sinnvoll, diesen Default-Kopier-Konstruktor zu re-definieren • Verwendung von dynamischen Datenelementen Bruch summe( Bruch, Bruch ); class Bruch { private: int zaehler_; int nenner_; public: Bruch( int, int ); void display( ); }; expliziter Aufruf bei einer Zuweisung Bruch br1(3,4); Bruch br2 = br1; impliziter Aufruf bei Abarbeitung einer Funktion (/* Methode */) Bruch br1(3,4); Bruch br2(1,5); Bruch erg = summe(br1,br2); 9/11 9/12 Hinweis: der Default-Kopier-Konstruktor kopiert „einfach“ die Datenelemente Programmieren II Klassen Programmieren II Klassen Ergänzende Bemerkungen Ergänzende Bemerkungen Beispiel‘ class Polynom { private: Term * first_; public: Polynom( ); ~Polynom( ); void display( ); void add_term( int, int ); double evaluate( double ); }; Beispiel‘ impliziter Aufruf bei Abarbeitung einer Methode expliziter Aufruf bei einer Zuweisung class Polynom1 { private: Term * first_; public: Polynom1( ); ~Polynom1( ); void display( ); void add_term( int, int ); double evaluate( double ); void add_poly(Polynom1); }; Polynom poly1; Polynom poly2 = poly1; erzeugt Laufzeitfehler !!! Polynom1 poly1; Polynom1 poly2; poly1.add(poly2); erzeugt Laufzeitfehler !!! 9/13 9/14 dramatischer, weil man sich dieses Problem ggf. gar nicht bewußt ist !!! Programmieren II Klassen Programmieren II Klassen Ergänzende Bemerkungen Ergänzende Bemerkungen Beispiel‘ (/* Lösung 1 ... Problem umgehen */) Beispiel‘ (/* Lösung 2 */) class Polynom3 { private: Term* first_; public: Polynom3( ); ~Polynom3( ); Polynom3( Polynom3 & ); void add_term( int, int ); ... }; class Polynom2 { private: Term * first_; public: Polynom2( ); ~Polynom2( ); void display( ); void add_term( int, int ); double evaluate( double ); void add_poly(Polynom2 &); }; Deklaration des eigenen Kopier-Konstruktors wichtig!!! 9/15 Hinweis: immer von der Form 9/16 Klassenname( Klassenname & ); Programmieren II Klassen Programmieren II Klassen Ergänzende Bemerkungen Beispiel‘ siehe Konstruktor Ergänzende Bemerkungen Polynom3 poly1; Polynom3 poly2(poly1); Definition des eigenen Kopier-Konstruktors Polynom3::Polynom3( Polynom3 & poly ) { Term * dummy = new Term; (*dummy).set_ex(-1); (*dummy).set_ko(0); (*dummy).set_next(NULL); first_ = dummy; Term * help = poly.get_first(); help = (*help).get_next(); int e, k; while ( help != NULL ) { e = (*help).get_ex(); k = (*help).get_ko(); add_term(e,k); help = (*help).get_next(); } } poly1.first_ -1 0 0 3 3 -7 siehe Methode add_term 9/17 Programmieren II Klassen 9/18 Programmieren II Klassen Ergänzende Bemerkungen Ergänzende Bemerkungen Polynom3 poly1; Polynom3 poly2(poly1); Polynom3 poly1; Polynom3 poly2(poly1); poly1.first_ poly1.first_ -1 0 0 3 3 -7 poly2.first_ das macht C++ für uns; Erzeugung des Objekts vom Typ Polynom3 -1 0 -1 0 0 3 3 -7 poly2.first_ 9/19 9/20 Programmieren II Klassen Programmieren II Klassen Ergänzende Bemerkungen Ergänzende Bemerkungen Polynom3 poly1; Polynom3 poly2(poly1); Polynom3 poly1; Polynom3 poly2(poly1); help poly1.first_ -1 0 0 3 help poly1.first_ 3 -7 poly2.first_ -1 0 0 3 -1 0 0 3 3 -7 poly2.first_ -1 0 9/21 Programmieren II Klassen 9/22 Programmieren II Klassen Ergänzende Bemerkungen Ergänzende Bemerkungen Polynom3 poly1; Polynom3 poly2(poly1); Polynom3 poly1; Polynom3 poly2(poly1); help poly1.first_ -1 0 0 3 help poly1.first_ 3 -7 poly2.first_ -1 0 0 3 3 -7 -1 0 0 3 3 -7 poly2.first_ -1 0 0 3 9/23 9/24 Programmieren II Klassen Ergänzende Bemerkungen Polynom3 poly1; Polynom3 poly2(poly1); help poly1.first_ -1 0 0 3 3 -7 -1 0 0 3 3 -7 poly2.first_ 9/25