![]() |
Delphi-Version: 5
Interessantes Destruktor Problem
Dieser Thread ist ein Spin-Off von
![]() Dort hat Deep Sea folgenden Code gepostet:
Delphi-Quellcode:
Auf den ersten Bick sieht der Code korrekt aus, aber er trägt eine Zeitbombe in sich.
constructor TAbgeleiteteKlasse.Create;
begin FStream := TMemoryStream.Create; inherited Create(FStream); end; destructor TAbgeleiteteKlasse.Destroy; begin inherited; // Könnte ja auf den übergebenen Stream noch zugreifen. FStream.Free; end; Nach dem Aufruf von inherited im Destruktor in der gesamte Speicher des Objekts freigegeben. Es ist daher verboten nach diesem Zeitpunkt auf FStream zuzugreifen. Das MemoryStream-Objekt selbst ist zwar noch intakt, aber die Variable FStream ist nicht mehr gültig. In den allermeisten Fällen geht ein Zugriff auf diesen freigegebenen Speicher glimpflich ab. Wenn man allerdings einen Memory-Manager (FasttMM4) benützt und dieser den freigegebenen Speicher mit bestimmten Gardbytes füllt, dann wird das Problem offensichtlich. Oder wenn ein anderer Thread zufällig gerade den Speicher bekommt der soeben im Destruktor freigegeben wurde dann gibt das ganz bösartige Probleme. Ich behaupte also man darf so wie oben nicht programmieren und lade jeden ein sich zu überlegen, wie man das Problem umgehen könnte. |
AW: Interessantes Destruktor Problem
Ich glaub ich habe das Problem nicht ganz verstanden.
Imho ist es nichtmal OOP-konform, wenn ein Objekt nach seiner Selbstzerstörung noch etwas macht. |
AW: Interessantes Destruktor Problem
Naja es geht wahrscheinlich nur darum, dass im Destructor das inherited ganz am Schluss aufgerufen werden soll/muss und nicht wie sonst meistens am Anfang der Methode. Aber die Codevervollständigung von neueren Delphiversionen legt Destructoren direkt schon so an:
Delphi-Quellcode:
Ist also schon in der Codevervollständigung ein kleiner Wink mit dem Zaunpfahl ;)
destructor TKlasse.Destroy;
begin inherited; end; |
AW: Interessantes Destruktor Problem
@sx2008
wie kommst Du darauf daß der Code korrekt aussähe..:?::?: |
AW: Interessantes Destruktor Problem
Vererbung ist für diese Problem wohl nicht die Lösung.
Ein Composit(-Pattern) wäre besser geeignet. |
AW: Interessantes Destruktor Problem
Zitat:
Erzeuge mal eine Instanz der folgenden Klasse und gib sie wieder frei:
Delphi-Quellcode:
Ich bekomme folgende Meldungen:
type
TMyObject = class public destructor Destroy; override; procedure FreeInstance; override; end; destructor TMyObject.Destroy; begin ShowMessage('Destroy before'); inherited; ShowMessage('Destroy after'); end; procedure TMyObject.FreeInstance; begin ShowMessage('FreeInstance'); inherited; end; Destroy before Destroy after FreeInstance Da die Freigabe des Speichers erst in TObject.FreeInstance erfolgt, ist die Instanz während des gesamten Destroy noch existent. (Getestet in XE ohne FastMM4) |
AW: Interessantes Destruktor Problem
Zitat:
|
AW: Interessantes Destruktor Problem
Delphi-Quellcode:
Destroy von TGrundklasse:
constructor TGrundklasse.Create(AStream: TStream);
begin inherited Create; end; destructor TGrundklasse.Destroy; begin inherited; end; constructor TAbgeleiteteKlasse.Create; begin FStream := TMemoryStream.Create; inherited Create(FStream); end; destructor TAbgeleiteteKlasse.Destroy; begin inherited; // Könnte ja auf den übergebenen Stream noch zugreifen. FStream.Free; end;
Code:
Richtig, ClassDestroy, was FreeInstance aufrufen wird, steht hier drin. ABER: Durch den bedingten Sprungbefehl jle wird es hier nicht aufgerufen!
Unit1.pas.47: inherited;
0046491F 8BD3 mov edx,ebx 00464921 80E2FC and dl,$fc 00464924 8BC6 mov eax,esi 00464926 E8B9F1F9FF call TObject.Destroy Unit1.pas.48: end; 0046492B 84DB test bl,bl 0046492D 7E07 jle $00464936 // Springt um ClassDestroy herum. 0046492F 8BC6 mov eax,esi 00464931 E846F6F9FF call @ClassDestroy // Ruft FreeInstance auf. 00464936 5E pop esi 00464937 5B pop ebx 00464938 C3 ret 00464939 8D4000 lea eax,[eax+$00] Destroy von TAbgeleiteteKlasse:
Code:
Unit1.pas.58: inherited;
00464993 8BD3 mov edx,ebx 00464995 80E2FC and dl,$fc 00464998 8BC6 mov eax,esi 0046499A E875FFFFFF call TGrundklasse.Destroy Unit1.pas.59: FStream.Free; 0046499F 8B4604 mov eax,[esi+$04] 004649A2 E84DF1F9FF call TObject.Free Unit1.pas.60: end; 004649A7 84DB test bl,bl 004649A9 7E07 jle $004649b2 // Springt nicht. 004649AB 8BC6 mov eax,esi 004649AD E8CAF5F9FF call @ClassDestroy // Ruft FreeInstance auf. 004649B2 5E pop esi 004649B3 5B pop ebx 004649B4 C3 ret 004649B5 8D4000 lea eax,[eax+$00] @Neutral General: Zwingt dich auch keiner zu, wenn du schnell den Überblick verlierst. Aber falsch ist es immer noch nicht. Danke Uwe, endlich mal jemand der auf meiner Seite steht :-D |
AW: Interessantes Destruktor Problem
Zitat:
Es ist eher andersrum, also daß TAbgeleiteteKlasse auf Eigenschaften des Vorfahren zugreift. Und wenn FStream im Vorfahren deklariert ist, dann ist dieser für dessen Erzeugung und Freigabe verantwortlich, womit dieses nicht in den Nachfahren reingehören würde. Also im Constructur und anderen erzeugenden Routinen Inherited grundsätzlich als Erstes und im Destructor, sowie anderen freigebenden Routinen als Letzes. Und sonst je nach dem, wie Dinge aus dem Vorfahren gebraucht/erzeugt/freigegeben werden. |
AW: Interessantes Destruktor Problem
Zitat:
Abgesehen davon ist es deutlich sauberer das inherited in Destructoren immer (!) als letztes aufzurufen. So wird das Objekt ganz geordnet "von oben nach unten" abgeräumt. Wenn jeder sein inherited irgendwo an den Anfang oder in die Mitte des Destructors setzt ist es als würde man aus nem Turm irgendwo in der Mitte ein Stein rausziehen... (:arrow: ![]() |
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:54 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 by Thomas Breitkreuz