Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Wann Destruktor-Aufruf nötig? (https://www.delphipraxis.net/79689-wann-destruktor-aufruf-noetig.html)

Cöster 26. Okt 2006 14:36


Wann Destruktor-Aufruf nötig?
 
Hi!

In welchen Fällen ist es eigentlich sinnvoll, den Destruktor aufzurufen? Wenn ich z.B. eine Instanz beim Programmstart per FInstanz := TKlasse.Create; erzeugt habe und den Konstruktor später nochmal aufrufen möchte, um alle Eigenschaften der Instanz zurückzusetzen, ist es dann nötig, vorher die alte Instanz zu destroyen?

Wann ist der Aufruf von FInstanz.Destroy überhaupt angebracht? Nur beim Programmende?

hoika 26. Okt 2006 14:41

Re: Wann Destruktor-Aufruf nötig?
 
Hallo,

1. schreibe eine Methode Clear, die du sowohl im constructor
als auch bei Bedarf aufrufen kannst.

2. rufe nie Destroy direkt auf, sondern nimm Free.

Mit Clear brauchst du erst am Programmende Free aufrufen.


Heiko

Hador 26. Okt 2006 14:53

Re: Wann Destruktor-Aufruf nötig?
 
Zitat:

Zitat von Cöster
In welchen Fällen ist es eigentlich sinnvoll, den Destruktor aufzurufen? Wenn ich z.B. eine Instanz beim Programmstart per FInstanz := TKlasse.Create; erzeugt habe und den Konstruktor später nochmal aufrufen möchte, um alle Eigenschaften der Instanz zurückzusetzen, ist es dann nötig, vorher die alte Instanz zu destroyen?

Wenn es nur darum geht, einige Eigenschaften auf einen Ausgangswert zurückzusetzen, kannst du dies natürlich auch tun ohne das jeweilige Objekt zu zerstören.

Zitat:

Zitat von Cöster
Wann ist der Aufruf von FInstanz.Destroy überhaupt angebracht? Nur beim Programmende?

Erzeuge ein Objekt sobald du es brauchst und zerstöre es, wenn du es nicht mehr brauchst.

Cöster 26. Okt 2006 17:32

Re: Wann Destruktor-Aufruf nötig?
 
Warum zerstört Delphi eigentlich nicht automatisch alles am Programmende?

Khabarakh 26. Okt 2006 18:08

Re: Wann Destruktor-Aufruf nötig?
 
Du stellst dir das etwas zu einfach vor, dafür bräuchte Delphi schon einen richtigen (naja, > 3/4) Garbage Collector. Und außer bei Klassen, die ein Handle auf externe Ressourcen halten, ist das Freigeben bei Programmende sowieso egal.

DGL-luke 26. Okt 2006 18:13

Re: Wann Destruktor-Aufruf nötig?
 
Jop, um Khabarakhs Äußerung ein wenig zu präzisieren, Windows markiert Speicher, den ein Programm gehalten hatte, wieder als frei, wenn das Programm beendet wird.

Cöster 26. Okt 2006 18:34

Re: Wann Destruktor-Aufruf nötig?
 
Dann sind Meinungen wie "jede Klasse braucht einen Destruktor" doch eigentlich Quatsch. Bei Klassen, die das ganze Programm über gebraucht werden, ist es doch dann meistens unnötig, überhaupt einen zu deklarieren, oder?

DGL-luke 26. Okt 2006 18:38

Re: Wann Destruktor-Aufruf nötig?
 
Eine Klasse weiß nicht, wie lange sie gebraucht wird(OOP). Sie sollte deshalb alles, was sie alloziiert, auch wieder freigeben. Das ist guter Stil.

Und Pauschalaussagen sind zwar meist kein "Quatsch", aber es gibt trotzdem immer Ausnahmen ;)

PS: Schon TObject hat einen Destruktor. EIne Klasse ohne Destruktor gibts also gar nicht :stupid:

Cöster 26. Okt 2006 19:11

Re: Wann Destruktor-Aufruf nötig?
 
Zitat:

Zitat von DGL-luke
Schon TObject hat einen Destruktor. EIne Klasse ohne Destruktor gibts also gar nicht :stupid:

Macht es bei Klassen, die von TObject direkt abgeleitet sind eigentlich sinn, inherited Create bzw. Destroy aufzurufen? Denn in TObject.Create bzw. Destroy passiert ja nix.

jbg 26. Okt 2006 20:42

Re: Wann Destruktor-Aufruf nötig?
 
Zitat:

Zitat von Cöster
Macht es bei Klassen, die von TObject direkt abgeleitet sind eigentlich sinn, inherited Create bzw. Destroy aufzurufen? Denn in TObject.Create bzw. Destroy passiert ja nix.

Nötig ist es nicht, da beide nichts machen. Das kann sich aber ändern. Und bei Delphi.NET hat es sich bereits geändert. Der .NET Compiler meckert wenn du kein inherited Create aufrufst.
Ein anderer Punkt ist, wenn man später mal feststellt, dass die Klasse doch besser von einer anderen Erben soll. In diesem Fall kann ein fehlender inherited Create Aufruf zu Problemen führen.

hoika 27. Okt 2006 08:12

Re: Wann Destruktor-Aufruf nötig?
 
Hallo,

ich schreibe immer einen constructor,
auch wenn der nur inherited Create aufruft.

1. jede Klasse wird mal erweitert
2. weit wichtiger, da ich memcheck
benutze, zeigt er mir genau die Stelle, wo die Klasse erzeugt wird,
aber nicht mehr freigegeben wird.
3. siehe oben (das mit der neuen Basisklasse)


Heiko

Cöster 28. Okt 2006 08:48

Re: Wann Destruktor-Aufruf nötig?
 
Nun hab ich das Problem, dass beim Zurücksetzen der Eigenschaften einige Instanzen bereits zerstört sind, andere nicht. Dann funktioniert eine Methode wie Clear natürlich nicht. Sollte ich dann doch alle Objekte wieder neu erstellen? Hab ich Beitrag #3 richtig verstanden, dass ich vor dem erneuten Create nicht erst Destroy aufzurufen brauche?

r2c2 28. Okt 2006 10:07

Re: Wann Destruktor-Aufruf nötig?
 
Zitat:

Zitat von Cöster
Nun hab ich das Problem, dass beim Zurücksetzen der Eigenschaften einige Instanzen bereits zerstört sind, andere nicht. Dann funktioniert eine Methode wie Clear natürlich nicht.

Mach mal n Beispiel. Weiß nicht so ganz, wie du das meinst...

Zitat:

Sollte ich dann doch alle Objekte wieder neu erstellen? Hab ich Beitrag #3 richtig verstanden, dass ich vor dem erneuten Create nicht erst Destroy aufzurufen brauche?
Ähm... bitte nicht zum Eigenschaften zurücksetzen Create aufrufen. Schreib dir lieber ne Methode Reset oder sowas... Warum? Manchmal geht das gut, jo. Wenn aber sowas im Konstruktor steht, produzierst du Speicherlöcher:
Delphi-Quellcode:
constructor TTest.Create;
begin
  inherited Create;
  FFeld := wert;
  ...
  FStringList := TStringList.Create; // <--
  ...
end;
Warum sollte man nun trotzdem Speicher freigeben, auch, wenn der Speichermanager hinterher wieder aufräumt? Ganz einfach: Du kippst doch auch nicht deinen Mülleimer aus dem Fenster, nur, weil du weißt, dass irgendwann jemand kommt, der das aufräumt... Es ist einfach schlechter Programmierstil.

Unter .NET is das was anderes: Da is die Putzfrau schon bestellt um nach der virtuellen Party die verloren gegangenen Bits wieder zusammenzukehren...

mfg

Chriostian

Cöster 28. Okt 2006 10:34

Re: Wann Destruktor-Aufruf nötig?
 
Zitat:

Zitat von r2c2
Zitat:

Zitat von Cöster
Nun hab ich das Problem, dass beim Zurücksetzen der Eigenschaften einige Instanzen bereits zerstört sind, andere nicht. Dann funktioniert eine Methode wie Clear natürlich nicht.

Mach mal n Beispiel. Weiß nicht so ganz, wie du das meinst...

16 Objekte werden in einer von TObjectList abgeleiteten Klasse verwaltet. Während das Programm läuft, wird gelegentlich ein Objekt aus der ObjektList per Delete gelöscht. Wenn auf btnNeu geklickt wird, sollen sich wieder alle 16 Objekte in der ObjektList befinden und die Eigenschaften sollen zurückgesetzt werden. Es soll also wieder genau der Stand vom Anfang zurückgestellt werden.

Mit der Methode Clear meinte ich die von das von dir angesprochene Reset, hab's nur Clear genannt, weil Hoika es in Beitrag #2 so genannt hatte. Ich kann aber ja schlecht Reset für ein Objekt aufrufen, welches bereits gelöscht wurde. Dieses Objekt müsste ich erst wieder neu erstellen.

Deswegen wollte ich gleich alle Objekte neu erstellen. Haltet ihr es für geschickter, zu prüfen, welche Objekte noch bestehen und für diese Reset aufzurufen und nur die anderen zu createn?

Daniel 28. Okt 2006 10:38

Re: Wann Destruktor-Aufruf nötig?
 
öhm ...
Also wenn Du ein Objekt aus der Liste löschst, solltest Du erst dessen .Free-Methode aufrufen und es dann tatsächlich aus der Liste entfernen. Diese hat danach eben ein Element weniger.

Und wenn Du wieder auf 16 Elemente kommen möchtest, dann füllst Du eben auf. Aber auf diese Weise bist Du sicher, dass die Elemente, die noch in der Liste sind, definitiv auch benutzbar sind. Alles Andere wäre aus meiner Sicht softwaretechnische Kamikaze.

Wie merkst Du Dir denn jetzt, auf welche Positionen Du noch zugreifen kannst und auf welche nicht? Oder vertraust Du auf das Glück des Anwenders? ;-)

r2c2 28. Okt 2006 10:40

Re: Wann Destruktor-Aufruf nötig?
 
Zitat:

Zitat von Cöster
Deswegen wollte ich gleich alle Objekte neu erstellen. Haltet ihr es für geschickter, zu prüfen, welche Objekte noch bestehen und für diese Reset aufzurufen und nur die anderen zu createn?

Kommt drauf an, wie viele Objekte das sind:
Hast du noch viele Objekte da, isses schneller diese zurückzusetzen, hast du wenige, isses schneller alle neu zu erstellen.

In Anbetracht dessen, dass du nur ObjectList.Clear; aufrufen musst(OwnsObjects ist true) um alle Objete frei zu geben, und du das Erstellen aller objekte in ne separate Prozedur packen kannst, isses aber wohl deutlich einfacher alles neu zu erstellen...

mfg

Christian

Jens Schumann 28. Okt 2006 10:41

Re: Wann Destruktor-Aufruf nötig?
 
Zitat:

Zitat von Cöster
Hab ich Beitrag #3 richtig verstanden, dass ich vor dem erneuten Create nicht erst Destroy aufzurufen brauche?

Wenn Du ein Objekt erzeugt hast mußt Du grundsätzlich vor einem erneuten Create den Destructor aufrufen. Der Destructor wird über die Methode Free aufgerufen. Wenn Du das nicht machst produzierst Du Speicherlöcher!!!
Delphi-Quellcode:
Beispielprocedure TForm1.Button1Click(Sender: TObject);
var
  MyList : TStrings;
begin
1. MyList := TStringlist.Create; // Hier wird das Objekt MyList erzeugt und belegt Arbeitsspeicher
                                  // In MyList wird eine Referenz (Zeiger) auf den reservierten
                                  // Arbeitsspeicher abgelegt.
2. MyList.Add('Test');
3. MyList := TStringlist.Create; // Hier wird ebenfalls das Objekt MyList erzeugt und belegt Arbeitsspeicher
                                  // Was passiert aber mit dem unter 1. reservierten Arbeitsspeicher?
                                  // Gar nichts. Der Arbeitsspeicher wird durch das Create unter 3. weder
                                  // freigeben noch überschrieben. D.h. der Arbeitsspeicher bleibt reserviert und belegt
                                  // Da jetzt durch 3. MyList einen anderen Arbeitsspeicherblock referenziert ist der
                                  // Arbeitsspeicher von 1. für Dein Programm nicht mehr erreichbar und bleibt bis zum
                                  // Programmende belegt.
4. MyList.Add('Delphipraxis');
end;
Faustregel 1:
Code:
Was Du erzeugst musst Du auch wieder zerstören
Faustregel 2:
Code:
Vor jedem erneuten Create die Methode Free aufrufen

Cöster 28. Okt 2006 11:11

Re: Wann Destruktor-Aufruf nötig?
 
Zitat:

Zitat von Daniel
Wie merkst Du Dir denn jetzt, auf welche Positionen Du noch zugreifen kannst und auf welche nicht? Oder vertraust Du auf das Glück des Anwenders? ;-)

Bei Delete wird doch Count um 1 verringert und alle Elemente 1 nach vorne gesetzt, oder? In allen meinen Schleifen hab ich stehen for I := 0 to Count - 1. Ich dachte eigentlich, dass dadurch alles glatt laufen würde. Aber komischerweise kommt trotzdem immer eine "Exception der Klasse EListError mit der Meldung 'Listenindex überschreitet das Maximum (15)'". Irgendwas mach ich da falsch. Ich weiß aber auch nicht, wo genau der Fehler auftritt, denn der Cursor wird nicht in die Zeile des Fehlers gesetzt, sondern an die Stelle, wo die Prozedur aufgerufen wird, innerhalb der der Fehler entsteht. Kann man das irgendwie irgendwo anders einstellen?

Inzwischen denke ich mir aber, vielleicht geb ich den Objekten ne Boolean-Eigenschaft, unter der ich speicher, ob mit dem Objekt noch etwas gemacht werden soll. Dann behalte ich immer meine 16 Objekte und könnte auch später bequem die Reset-Methode aufrufen.

Aber wie das mit dem Delete genau funzt, würde mich trotzdem interessieren.

@ Jens Schumann: Danke, die Faustregeln werd ich mir merken :wink:

Hawkeye219 28. Okt 2006 11:32

Re: Wann Destruktor-Aufruf nötig?
 
Hallo Cöster,

wenn du innerhalb einer Schleife Elemente aus einer Liste (TList, TObjectList,...) entfernen möchtest, solltest du die Schleife rückwärts laufen lassen. Da beim Löschen die Listenlänge verändert wird, greifst du sonst bei den letzten Schleifendurchläufen auf nicht mehr gültige Elemente zu. Der Schleifenendwert wird vor dem Start der Schleife ermittelt und durch das Löschen nicht verändert.

Gruß Hawkeye

Cöster 28. Okt 2006 11:54

Re: Wann Destruktor-Aufruf nötig?
 
Zitat:

Zitat von Daniel
Also wenn Du ein Objekt aus der Liste löschst, solltest Du erst dessen .Free-Methode aufrufen

Passiert das bei einer TObjectList nicht automatisch, wenn OwnsObjects wie voreingestellt auf True gesetzt ist?

@ Hawkeye219: Ah, klingt logisch, das war auch der Fehler. Wundert mich nur, dass der Cursor beim Auftreten des Fehlers immer zu dem Aufruf einer anderen Methode gesprungen ist, in der der Fehler dann gar nicht aufgetreten ist.

Hawkeye219 28. Okt 2006 12:10

Re: Wann Destruktor-Aufruf nötig?
 
Zitat:

Zitat von Cöster
Zitat:

Zitat von Daniel
Also wenn Du ein Objekt aus der Liste löschst, solltest Du erst dessen .Free-Methode aufrufen

Passiert das bei einer TObjectList nicht automatisch, wenn OwnsObjects wie voreingestellt auf True gesetzt ist?

Du hast Recht, in diesem Fall hat sich Daniel geirrt.

Zitat:

Wundert mich nur, dass der Cursor beim Auftreten des Fehlers immer zu dem Aufruf einer anderen Methode gesprungen ist, in der der Fehler dann gar nicht aufgetreten ist.
Nach einem Laufzeitfehler steht der Cursor meistens nicht in der Zeile, die den Fehler enthält, sondern in der darauf folgenden Zeile.

Gruß Hawkeye

Cöster 28. Okt 2006 12:14

Re: Wann Destruktor-Aufruf nötig?
 
Zitat:

Zitat von Hawkeye219
Nach einem Laufzeitfehler steht der Cursor meistens nicht in der Zeile, die den Fehler enthält, sondern in der darauf folgenden Zeile.

Aha. Bei mir war es aber die darauffolgende Zeile in Unit1 und nicht in der Unit, in der die Schleife durchgeführt wird. Kann man diese Einstellung irgendwo ändern? Sonst muss ich mir ja die gesamte Methode angucken, die teilweise ziemlich verschachtelt ist.

Hawkeye219 28. Okt 2006 12:25

Re: Wann Destruktor-Aufruf nötig?
 
Ich kenne keine Möglichkeit, diese Einstellung zu ändern. MaBuSE hat ein sehr gutes Tutorial zum Thema Debugging geschrieben. Vielleicht findest du dort weitere Informationen.

Gruß Hawkeye


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:46 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz