Wenn in Delphi ein Objekt aus dem Leben scheidet dann gibt es nur einen einzigen Weg dies zu tun -
über den Destruktor.
Borland hat den Destruktor schon in der Vaterklasse TObject deklariert
Delphi-Quellcode:
TObject = class
...
public
...
destructor Destroy; virtual;
end;
Zu jeder Klasse gibt eine
VMT (virtual Method Table) und die Destroy-Methode hat dort einen festen Eintrag.
Der Index innerhalb der
VMT ist mit der Konstanten vmtDestroy=-4 festgelegt.
Es gibt nur einen einzigen Destruktor.
Daraus kann man die Nebenbedingungen schlussfolgern
* der Destructor muss immer
Destroy heisen
* der Destruktor kann
keine Parameter haben und er hat auch keinen Rückgabewert
* der Destruktor muss immer mit
override überschrieben werden
* der Destruktor darf keine eingeschränkte Sichtbarkeit haben; er ist also immer
public
destructor Destroy; override;
Jede Abweichung von dieser Deklaration ist falsch.
Hier ist ein Beispiel mit mehreren falsch deklarierten Destruktoren:
Delphi-Quellcode:
TMeineKlasse =
class(TPersistent)
private
destructor Destroy;
// Falsch - override fehlt, darf nicht private sein
public
destructor Destroy;
override;
// Richtig
destructor GibFrei;
// Falscher Name
destructor Destroy; reindroduce;
// Falsch da so die VMT ignoriert wird
end;
Warum hat der Destruktor überhaupt einen Namen, wenn es sowieso nur einen einzigen gibt?
Hätte der Destruktor keinen Namen, gäbe es keine Möglichkeit, ihn direkt aufrufen
Warum ist der Destruktor mit der Sichtbarkeit public deklariert, hätte man ihn nicht auch protected machen können?
Borland wollte offensichtlich dem Programmierer die Möglichkeit geben, den Destruktor direkt aufzurufen.
Warum gibt es eigentlich nur einen einzigen Destruktor?
Wenn ein Objekt freigeben wird, dann wird es nicht mehr gebraucht.
Derjenige, der das Objekt freigibt hat oftmals kein Wissen um was für ein Objekt es sich handelt.
Genauer gesagt, der Code, der ein Objekt freigibt soll gar keine näheren Infos zu Internas des Objekt haben. (Geheimnisprinzip des
OOP)
Der Besitzer des Objekts sagt "Stirb!" und das Objekt hat zu gehorchen.
Was ist der Unterschied zwischen Free und Destroy?
Free ruft Destroy auf, abr nur wenn das Objekt überhaupt existiert; also der self-Pointer nicht nil ist.
Am Besten sieht man das, wenn man sich die Free-Prozedur anschaut.
Delphi-Quellcode:
procedure TObject.Free;
begin
if Assigned(self) then Destroy;
end;
Darf man Destroy überhaupt aufrufen? Es heisst doch immer man solle Free verwenden.
Ja, man darf Destroy direkt aufrufen und man kann so einige CPU-Takte sparen.
Aber man darf dies nur tun, wenn man 1000% sicher ist dass man das Objekt selbst erzeugt hat:
Delphi-Quellcode:
var
meinobj : TMeineKlasse;
begin
meinobj := TMeineKlasse.Create;
try
meinobj.Machwas;
meinobj.MasWasAnderes(42);
finally
// ja, das ist erlaubt aber nicht empfehlenswert
// man spart hier die Überprüfung ob meinobj <> nil ist
// aber es besteht die Gefahr, dass der Code mal verändert wird
// und dann die Änderung von Destroy nach Free vergessen wird
meinobj.Destroy;
end;
end;
Welcher Code gehört in einen Destruktor?
Auf jeden Fall sollte der Aufruf von Inherited die letzte Zeile im Destruktor sein.
Delphi-Quellcode:
destructor TMeinKlasse.Destroy;
begin
FList.Free;
inherited;
end;
Man sollte im Destruktor nur belegte Resourcen (also eingebette Objekte, Window-Handles) freigeben.
Grössere Aktionen wie z.B. Speichern in einer Datenbank sollten vermieden werden.
Soll ich einen Destruktor auch dann verwenden, wenn gar nicht zu tun ist?
Nein, ein Destruktor sollte man nur dann deklarieren, wenn auch etwas zu tun ist.
Delphi-Quellcode:
// schlechter Stil: unnötiger Code und unnötiger Eintrag in der VMT
destructor TMeineKlasse.Destroy;
begin
inherited;
end;
Wenn ich direkt von TObject ableite, dann brauche ich doch das inherited im Destruktor gar nicht aufrufen, oder?
Das ist im Prinzip richtig, aber man sollte als Programmierer nicht versuchen übertrieben clever zu sein.
Niemand weiss nicht ob nicht jemand in Zukunft die Klasse von einer anderen Basisklasse ableitet
und dann entsteht ein Resourcen-/Speicherleck.
Was sind die häufigsten Fehler beim Destruktor?
Das override bei der Deklaration zu vergessen oder das inherited innerhalb des Destruktor zu vergessen sind die Klassiker.
Warum braucht man überhaupt das inherited im Destruktor? Könnte Delphi das nicht automatisch aufrufen?
Gute Frage! In anderen Programmiersprachen (z.B. C++, C#) ist es tatsächlich so,
dass automatisch die ererbten Destruktoren aufgerufen werden, ohne dass sich der Programmierer darum kümmern müsste.