Alexander F. schrieb:
> Laut meinen Vorlesungsunterlagen (es gibt exakt zwölf Halbsätze zu
> Destruktoren, woraus ich nicht wirklich schlau werde) ist der
> Destruktor da um ein Objekt zu löschen, allozierten Speicherplatz
> wieder freizugeben und wird vom Compiler nach Ende der Objektlebensdauer
> aufgerufen
Ja, das ist korrekt.
> bzw. kann explizit aufgerfen werden.
Ich würd's eher so ausdrücken: Man kann das Objekt explizit zerstören,
ohne dabei den Speicher freizugeben. Dabei wird der Destruktor
aufgerufen.
> Jetzt erzeuge ich mir mehrere Instanzen des Objekts:
> Bruch *testBruch = new Bruch[5];
>
> Hier ist ja testBruch ein Pointer auf das erste Objekt und
> es existieren 5 Instanzen auf die ich einzeln zugreifen kann.
Richtig.
> Wie erzeuge ich jetzt einen Destruktor, der ein Objekt "zerstört" und
> den Speicher wieder freigibt? Ich weiß ja in der Klasse Bruch nicht
> welche Instanzen überhaubt existieren.
Es geht nicht um den Speicher, der für das Objekt selbst reserviert ist,
sondern um Speicher bzw. allgemeiner alle Ressourcen, die deine Klasse
intern verwaltet. Das gehört zum Thema Kapselung. Die Objekte deiner
Klasse halten intern irgendwelche Ressourcen, an die man von außen in
der Regel gar nicht direkt ran kommen soll, die aber spätestens zum Ende
der Lebensdauer des Objekts freigegeben werden müssen.
Nehmen wir mal an, dein Bruch würde Zähler und Nenner dynamisch
allokieren. Das wäre zwar wenig sinnvoll, aber verdeutlicht hier das
Problem:
1 | class Bruch
|
2 | {
|
3 | ...
|
4 | public:
|
5 | Bruch();
|
6 | setZahler(int z)...;
|
7 | setNenner(int n)...;
|
8 | private:
|
9 | int* zaehler;
|
10 | int* nenner;
|
11 | };
|
12 | |
13 | Bruch()
|
14 | {
|
15 | zahler = new int(0);
|
16 | nenner = new int(1);
|
17 | }
|
Hier werden Zähler und Nenner im Konstruktor dynamisch allokiert, wenn
dein Bruch erzeugt wird. Aber wo werden sie wieder freigegeben? Dein
delete [] testBruch gibt die Bruch-Objekte selbst wieder frei und den
Speicher, der für die Zeiger benötigt wird, aber die von dir im
Konstruktor dynamisch erzeugten ints sind noch da, und du hast die
Zeiger darauf verloren, hast also auch keine Chance mehr, sie jemals
wieder freizugeben. Und genau da kommt der Destruktor ins Spiel:
1 | ~Bruch()
|
2 | {
|
3 | delete zaehler;
|
4 | delete nenner;
|
5 | }
|
Und das bezieht sich natürlich nicht nur auf dynamischen Speicher,
sondern auf alle Ressourcen, wie z.B. geöffnete Dateien oder bei einer
GUI ein Fenster-Handle. Spätestens wenn das Fenster-Objekt zerstört
wird, muss auch das Handle und alle Ressourcen, die zum Fenster gehören,
wieder freigegeben werden.
> Mein Ansatz für das Problem stammt jetzt aus der dynamischen
> Speicherverwaltung, dort heißt es, dass man Speicher wieder freigibt,
> indem man den Zeiger auf das reservierte Array löscht.
Ja.
> In meinem Fall rufe ich, wenn ich die Objekte nicht mehr brauche
> delete [] testBruch;
> mitten im Programm auf. Ob das überhaupt funktioniert weiß ich nicht.
Das funktioniert.
> Dass das nicht richtig sein kann, ist mir aber schon klar, sonst
> gäbe es die Destruktoren ja nicht, ich habe trotzdem wirklich keine Idee
> wie das richtig funktioniert.
Es ist in deinem Fall richtig, da du keine Ressourcen hast, die in der
Klasse verwaltet werden müssen.