Konstruktoren / Destruktoren

Preview:

DESCRIPTION

Konstruktoren / Destruktoren. In C: Initialsieren einer Variable, z.B: int i = 10;. In C++: Initialsieren eines Objekts. Dies geschieht durch den sogenannten Konstruktor. - PowerPoint PPT Presentation

Citation preview

Konstruktoren / Destruktoren

In C:Initialsieren einer Variable,

z.B:int i = 10;

In C++:Initialsieren eines Objekts

Dies geschieht durch den sogenannten Konstruktor.

Dies ist eine spezielle Methode in der Klasse mit

dem gleichen Namen wie die Klasse, aber ohne einen

Rückgabewert (auch nicht void).

Ein Konstruktor kann (wie jede andere Funktion auch) überladen sein, d.h. es kann mehrere Konstruktoren mit dem gleichen Namen, aber verschiedenen Parametern

-bezogen auf die Anzahl und den Datentyp - geben

Konstruktor wird immer dann aufgerufen, wenn ein

Objekt der Klasse instanziert wird.

Aufgabe:Erzeugen Sie für die Klasse

Punkt (in einem Koordinatensystem) einen Konstruktor, einmal mit keinem bzw. mit zwei

Parametern.

class Punkt{ private: int x; int y;

public: Punkt(); Punkt(int xx, int yy); void set_xy(int xx, int yy); int get_x(); int get_y();}; // class Punkt

Konstruktor kann überladen sein

Konstruktor hat keinen (auch nicht void) Rückgabewert.

Punkt::Punkt(){ x = 0; y = 1; }

Punkt::Punkt(int xx, int yy){ set_xy(xx, yy); }

void Punkt::set_xy(int xx, int yy){ x = xx; y = yy; } // gleich geht es weiter. ...

int Punkt:: get_x(){ return(x);}

int Punkt::get_y(){ return(y);}

Beispiele für das Instanzieren von Objekten:

class Punkt{//siehe oben};

int main(){ int kx, ky; Punkt p; Punkt q(3, 7); kx = p.get_x(); ky = p.get_y(); kx = q.get_x(); ky = q.get_y(); p.set_xy(2,4); q.set_xy(6,8);}

Konstruktor ohne Parameter wird ohne

Klammern aufgerufen

Konstruktor mit Parameter wird mit Klammern

aufgerufen

class Punkt{//siehe oben};

int main(){ Punkt p; Punkt q(3, 7); kx = p.get_x(); ky = p.get_y(); kx = q.get_x(); ky = q.get_y(); p.set_xy(2,4); q.set_xy(6,8);}

p.x p.y q.x q.y kx ky

class Punkt{//siehe oben};

int main(){ Punkt p; Punkt q(3, 7); kx = p.get_x(); ky = p.get_y(); kx = q.get_x(); ky = q.get_y(); p.set_xy(2,4); q.set_xy(6,8);}

p.x p.y q.x q.y kx ky

0 1 ? ? ? ?

class Punkt{//siehe oben};

int main(){ Punkt p; Punkt q(3, 7); kx = p.get_x(); ky = p.get_y(); kx = q.get_x(); ky = q.get_y(); p.set_xy(2,4); q.set_xy(6,8);}

p.x p.y q.x q.y kx ky

0 1 ? ? ? ?

class Punkt{//siehe oben};

int main(){ Punkt p; Punkt q(3, 7); kx = p.get_x(); ky = p.get_y(); kx = q.get_x(); ky = q.get_y(); p.set_xy(2,4); q.set_xy(6,8);}

p.x p.y q.x q.y kx ky

0 1 ? ? ? ?

0 1 3 7 ? ?

class Punkt{//siehe oben};

int main(){ Punkt p; Punkt q(3, 7); kx = p.get_x(); ky = p.get_y(); kx = q.get_x(); ky = q.get_y(); p.set_xy(2,4); q.set_xy(6,8);}

p.x p.y q.x q.y kx ky

0 1 ? ? ? ?

0 1 3 7 ? ?

class Punkt{//siehe oben};

int main(){ Punkt p; Punkt q(3, 7); kx = p.get_x(); ky = p.get_y(); kx = q.get_x(); ky = q.get_y(); p.set_xy(2,4); q.set_xy(6,8);}

p.x p.y q.x q.y kx ky

0 1 ? ? ? ?

0 1 3 7 ? ?

0 1 3 7 0 ?

class Punkt{//siehe oben};

int main(){ Punkt p; Punkt q(3, 7); kx = p.get_x(); ky = p.get_y(); kx = q.get_x(); ky = q.get_y(); p.set_xy(2,4); q.set_xy(6,8);}

p.x p.y q.x q.y kx ky

0 1 ? ? ? ?

0 1 3 7 ? ?

0 1 3 7 0 ?

class Punkt{//siehe oben};

int main(){ Punkt p; Punkt q(3, 7); kx = p.get_x(); ky = p.get_y(); kx = q.get_x(); ky = q.get_y(); p.set_xy(2,4); q.set_xy(6,8);}

p.x p.y q.x q.y kx ky

0 1 ? ? ? ?

0 1 3 7 ? ?

0 1 3 7 0 ?

0 1 3 7 0 1

class Punkt{//siehe oben};

int main(){ Punkt p; Punkt q(3, 7); kx = p.get_x(); ky = p.get_y(); kx = q.get_x(); ky = q.get_y(); p.set_xy(2,4); q.set_xy(6,8);}

p.x p.y q.x q.y kx ky

0 1 ? ? ? ?

0 1 3 7 ? ?

0 1 3 7 0 ?

0 1 3 7 0 1

class Punkt{//siehe oben};

int main(){ Punkt p; Punkt q(3, 7); kx = p.get_x(); ky = p.get_y(); kx = q.get_x(); ky = q.get_y(); p.set_xy(2,4); q.set_xy(6,8);}

p.x p.y q.x q.y kx ky

0 1 ? ? ? ?

0 1 3 7 ? ?

0 1 3 7 0 ?

0 1 3 7 0 1

0 1 3 7 3 1

class Punkt{//siehe oben};

int main(){ Punkt p; Punkt q(3, 7); kx = p.get_x(); ky = p.get_y(); kx = q.get_x(); ky = q.get_y(); p.set_xy(2,4); q.set_xy(6,8);}

p.x p.y q.x q.y kx ky

0 1 ? ? ? ?

0 1 3 7 ? ?

0 1 3 7 0 ?

0 1 3 7 0 1

0 1 3 7 3 1

class Punkt{//siehe oben};

int main(){ Punkt p; Punkt q(3, 7); kx = p.get_x(); ky = p.get_y(); kx = q.get_x(); ky = q.get_y(); p.set_xy(2,4); q.set_xy(6,8);}

p.x p.y q.x q.y kx ky

0 1 ? ? ? ?

0 1 3 7 ? ?

0 1 3 7 0 ?

0 1 3 7 0 1

0 1 3 7 3 1

0 1 3 7 3 7

class Punkt{//siehe oben};

int main(){ Punkt p; Punkt q(3, 7); kx = p.get_x(); ky = p.get_y(); kx = q.get_x(); ky = q.get_y(); p.set_xy(2,4); q.set_xy(6,8);}

p.x p.y q.x q.y kx ky

0 1 ? ? ? ?

0 1 3 7 ? ?

0 1 3 7 0 ?

0 1 3 7 0 1

0 1 3 7 3 1

0 1 3 7 3 7

class Punkt{//siehe oben};

int main(){ Punkt p; Punkt q(3, 7); kx = p.get_x(); ky = p.get_y(); kx = q.get_x(); ky = q.get_y(); p.set_xy(2,4); q.set_xy(6,8);}

p.x p.y q.x q.y kx ky

0 1 ? ? ? ?

0 1 3 7 ? ?

0 1 3 7 0 ?

0 1 3 7 0 1

0 1 3 7 3 1

0 1 3 7 3 7

2 4 3 7 3 7

class Punkt{//siehe oben};

int main(){ Punkt p; Punkt q(3, 7); kx = p.get_x(); ky = p.get_y(); kx = q.get_x(); ky = q.get_y(); p.set_xy(2,4); q.set_xy(6,8);}

p.x p.y q.x q.y kx ky

0 1 ? ? ? ?

0 1 3 7 ? ?

0 1 3 7 0 ?

0 1 3 7 0 1

0 1 3 7 3 1

0 1 3 7 3 7

2 4 3 7 3 7

class Punkt{//siehe oben};

int main(){ Punkt p; Punkt q(3, 7); kx = p.get_x(); ky = p.get_y(); kx = q.get_x(); ky = q.get_y(); p.set_xy(2,4); q.set_xy(6,8);}

p.x p.y q.x q.y kx ky

0 1 ? ? ? ?

0 1 3 7 ? ?

0 1 3 7 0 ?

0 1 3 7 0 1

0 1 3 7 3 1

0 1 3 7 3 7

2 4 3 7 3 7

2 4 6 8 3 7

0400 2p.y

0408 6q.x0412 8q.y

0404 4p.x

Mögliche Belegung im Arbeitsspeicher:(Annahme: Compiler benötigt 4 Byte für eine Integer-Variable

p

q

Adresse Inhalt

...

Ein Konstruktor ohne Argumente (Parameter) wie z.B. Punkt() heisst Standard-Konstruktor.

Implementiert der Programmierer für eine Klasse überhaupt keinen Konstruktor, dann erzeugt der Compiler automatisch einen Standard-Konstruktor, der keine Anweisungen besitzt.

Hat der Programmierer dagegen mindestens einen Konstruktor (egal mit 0, 1, 2,..., n Parametern) implementiert, erzeugt der Compiler keinen Standard-Konstruktor.

Beispiel:

class Nagel{ private: int laenge;  public: void set(int l);};

void Nagel::set(int l){ laenge = l;}

int main(){ Nagel n;}

Ist dies korrekt ?Warum ?

Ja, weil der Compiler automatisch einen Standard-Konstruktor erzeugt.

Wie lang ist der Nagel, d.h. welchen Wert hat n.laenge ?

Man weiß es nicht, weil der vom Compiler erzeugte Standard-Compiler keine Anweisung besitzt.

Beispiel:

class Nagel{ private: int laenge;  public: Nagel(int l) void set(int l);};

void Nagel::set(int l){ laenge = l;}

Nagel::Nagel(int l){ set(l);}

int main(){ Nagel n;}

Ist dies korrekt ?Warum ?

Nein, weil der Compiler keinen Standard-Konstruktor erzeugt.

EinStandard-Konstruktor wird aber

beim Instanzieren bei:Nagel n;benötigt !

Konstruktoren:Wie wird sichergestellt, dass ein Objekt beim Instanzieren auf jeden Fall initialisiert wird, auch wenn der Programmierer keinen Konstruktor implementiert ?

Der Compiler erzeugt einen Standard-Konstruktor.

Problem mit Konstruktoren:Objekt enthält als Attribut wieder ein Objekt.Wie ist sichergestellt, dass dieses (eingelagerte) Objekt auch initialisiert wird, selbst wenn der Programmierer es vergisst ?

Beispiel:

class Punkt{ // wie früher};

class Kreis{ private: Punkt mp; int r;

public: Kreis(int xm, int ym, int a);

}; //class Kreis

Kreis::Kreis(int xm, int ym, int a){ r = a;}

Der Mittelpunkt ist ein Punkt:mp hat den Klassentyp (d.h. Typ ist

eine Klasse) Punkt

Was passiert, wenn der Programmierer den Punkt mp nicht im Konstruktor

Kreis initialisiert ?

Der Radius ist eine Integer-Zahl

class Punkt{ // wie früher};

class Kreis{ private: Punkt mp; int r;

public: Kreis(int xm, int ym, int a);

}; //class KreisUnabhängig vom Programmierer werden die eingelagerten Objekte auf jeden Fall vor Eintritt in den Funktionskörper des umgebenden Konstruktors (hier: Kreis) automatisch durch den jeweils zugehörigen Standard-Konstruktor (hier Punkt) jedes eingelagerten Objekts initialisiert.

class Punkt{ // wie früher};

class Kreis{ private: Punkt mp; int r;

public: Kreis(int xm, int ym, int a);

}; //class Kreis

Was passiert also, wenn der Programmierer in der Klasse Punkt noch keinen Standard-Konstruktor, aber einen anderen Konstruktor, egal mit wieviel (1, 2, 3, ...) Parametern, implementiert hat ?

Dann erzeugt der Compiler eine Fehlermeldung:Da schon ein Konstruktor existiert, erzeugt der Compiler keinen Standard-Konstruktor mehr. Doch diesen würde er benötigen.

Alternative Möglichkeit der Initialisierung eingelagerter Objekte:In der Initialisierungsliste werden die eingelagerten Objekte initialisiert (durch die zugehörigen Konstruktoren

Beispiel:

class Punkt{ // wie früher};

class Kreis{ private: Punkt mp; int r;

public: Kreis(int xm, int ym, int a);};

Kreis::Kreis(int xm, int ym, int a):mp(xm, ym){ r = a;}

Durch einen Doppelpunkt gefolgt von der Initialisierungsliste werden die eingelagerten Objekte initialisiert.

(durch die zugehörigen Konstruktoren)

Dies geschieht vor Eintritt in den Funktionskörper des umgebenden

Konstruktors (hier: Kreis)

int main(){ Kreis k1(10,20,30); Kreis k2(9,7,8);}

Welche Werte haben?k1.mp.x k1.mp.yk1.r

also:k1.mp.x: 10 k1.mp.y: 20k1.r: 30

Es wird aufgerufen:k1(10,20,30) --> k1.mp(10,20)

0300 10k1.mp.y

0308 30k1.r0304 20

k1.mp.x

Mögliche Belegung im Arbeitsspeicher:(Annahme: Compiler benötigt 4 Byte für eine Integer-Variable

k1

Adresse Inhalt

...

0312 9k2.mp.y

0320 8k2.r0316 7

k2.mp.xk2

Destruktor

Ein Destruktor ist in gewisser Weise das Gegenteil eines Konstruktors, weil evtl. vorher im Konstruktor angeforderte Ressourcen (z.B. allokierter Speicherplatz, geöffnete Dateien, usw.) im Destruktor wieder freigegeben werden müssen.

Ein Destruktor wird meist dann gebraucht, wenn ein dynamisch allokierter Speicher wieder freigegeben, wenn Dateien wieder geschlossen werden sollen, usw.

Ein Destruktor ist eine spezielle Methode in der Klasse mit dem gleichen Namen wie die Klasse und einer vorangestellten Tilde ~ Er hat keinen Parameter und ist ohne einen Rückgabewert (auch nicht void).

Jede Klasse kann nur einen Destruktor haben. Er wird automatisch aufgerufen, wenn ein Objekt ungültig (zerstört) wird.

Dies ist bei Objekten auf dem Stack im Allgemeinen bei der schließenden Klammer einer Funktion der Fall. Hierzu gehört auch int main(){...}

Den Destruktoraufruf kann man sich in der schließenden Klammer der Funktion konzentriert sehen.

In der schließenden Klammer } werden Destruktoren für Variable eines Klassentyps aufgerufen, nicht aber für Zeigervariable eines Klassentyps.

Ein Destruktor ohne Anweisungen wie z.B.

~Punkt(){}

heisst Standard-Destruktor.

Implementiert der Programmierer für eine Klasse keinen Destruktor, dann erzeugt der Compiler automatisch den Standard- Destruktor, der keine Anweisungen besitzt.

Beispiel

class Test{ public: Test(); Test(int i); ~Test();}; // class Test

benötigt keine Attribute (zu Testzwecken)

Test::Test(){ printf("SK aufgerufen\n");}

Test::Test(int i){ printf("SK für int mit Wert %d aufgerufen\n",i);}

Test::~Test(){ printf("Destruktor aufgerufen\n");}

SK bedeutet Standardkonstruktor

void machwas(){ printf("Start von machwas\n"); Test t; printf("Ende von machwas\n");}

int main(){ printf("Vor Aufruf von machwas\n"); machwas(); printf("Nach Aufruf von machwas\n");}

In der schließenden Klammer } wird der Destruktor der Klasse Test aufgerufen.

Welche Ausgaben erscheinen auf dem

Bildschirm ?

Vor Aufruf von machwas

Start von machwas

SK aufgerufen

Destruktor aufgerufen

Nach Aufruf von machwas

Ende von machwas

Präzisierung zu vorher (Objekte auf dem Stack):Ein Destruktor wird nicht nur bei der schließenden Klammer einer Funktion, sondern ganz allgemein bei der schließenden Klammer eines Blocks aufgerufen.

Beispiel:

void machwas(){ int i; for(i=0;i<3;i++){ Test t(i); }}

int main(){ printf("Vor Aufruf von machwas\n"); machwas(); printf("Nach Aufruf von machwas\n");}

Objekt wird innerhalb eines Blocks definiert

Destruktor wird automatisch aufgerufen

Klasse Test wie im obigen Beispiel

Welche Ausgaben erscheinen auf dem

Bildschirm ?

Vor Aufruf von machwas SK von int mit 0 aufgerufen

Destruktor aufgerufen

Destruktor aufgerufen

SK von int mit 2 aufgerufen

SK von int mit 1 aufgerufen

Destruktor aufgerufen

Nach Aufruf von machwas

Konstruktoren und Felder (Arrays)

Wird ein Feld von Objekten erzeugt, wird für jedes Element des Feldes ein Konstruktor aufgerufen.

An die Stelle eines Initialisierungswerts tritt nun

eine Liste von Werten.

Initialisieren mit einem Standard-Konstruktor.

(Konstruktor mit keinem Parameter).

Beispiel:

class Punkt{ private: int x; int y;

public: Punkt(); ~Punkt();};

Punkt::Punkt(){ x = 10; y = 20;}

Punkt::~Punkt(){}

int main(){

Wenn ein Standard-Konstruktor vorhanden ist (bzw.falls dieser vom Compiler angelegt wird, falls noch kein Konstruktor vom Programmierer erstellt wurde), kann die Initialisiererliste komplett wegfallen:

Punkt p[5];

}

Punkt q[5]={Punkt(),Punkt(),Punkt(), Punkt(), Punkt()};

Die folgende Initialisierung ist genauso möglich

Nach der Initialisierung gilt:p[0].x=10, p[0].y=20, p[1].x=10, p[1].y=20, p[2].x=10, p[2].y=20, p[3].x=10, p[3].y=20,p[4].x=10, p[4].y=20

Initialisieren mit einem Konstruktor mit einem

Parameter.

Beispiel:

class Punkt{ private: int x, y;

public: Punkt(int p); Punkt(int kx, int ky); ~Punkt();};

Punkt::Punkt(int p){ x = p; y = p;}

Punkt::Punkt(int kx, int ky){ x = kx; y = ky;}

Punkt:: ~Punkt(){}

int main(){

Wenn ein Konstruktor mit einem Parameter existiert, dann kann die Initialisiererliste aus durch Komma getrennten Werten bestehen.

Punkt p[4] = {13, 17, 19, 5};

}

Punkt q[4] = {Punkt(13),Punkt(17), Punkt(19),Punkt(5)};

Die folgende Initialisierung ist genauso möglich:

Nach der Initialisierung gilt:p[0].x=13, p[0].y=13, p[1].x=17, p[1].y=17, p[2].x=19, p[2].y=19, p[3].x=5, p[3].y=5

Initialisieren mit einem Konstruktor mit mehr als

einem Parameter.

Beispiel:

class Punkt{ private: int x; int y;

public: Punkt(int p); Punkt(int kx, int ky); ~Punkt();};

Punkt::Punkt(int p){ x = p; y = p;}

Punkt::Punkt(int kx, int ky){ x = kx; y = ky;}

Punkt::~Punkt(){}

int main(){

Wenn man einem Konstruktor mehr als einen Wert übergeben möchte,muß die Initialisiererliste wie folgt aussehen:

Punkt r[3] = {Punkt(2,3), Punkt(7,8), Punkt(4,9)}; }

Nach der Initialisierung gilt:p[0].x=2, p[0].y=3, p[1].x=7, p[1].y=8, p[2].x=4, p[2].y=9

Recommended