Delphi-PRAXiS
Seite 2 von 3     12 3      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Objekte freigeben (https://www.delphipraxis.net/129783-objekte-freigeben.html)

sirius 25. Feb 2009 18:36

Re: Objekte freigeben
 
Zitat:

Zitat von mjustin

wirft eine 'Externe Exception C00001D' (in D6, und etwas ähnliches in D2009).

Warnungen oder Hinweise wegen der nicht initialisierten Variable X gibt es keine - da muss man halt aufpassen :)

Bei lokalen Variablen ist das auch klar. Das tritt aber nur bei lokalen Variablen auf. Und da passt man sowieso auf. bzw. hat die entsprechende Methode im Überblick.

himitsu 25. Feb 2009 18:46

Re: Objekte freigeben
 
Variablen sollte man eh immer Initialisieren. (bevor man lesend drauf zugreifen könnte)

sx2008 25. Feb 2009 19:49

Re: Objekte freigeben
 
Diejenige Klasse, die eine oder mehrere Objekte erzeugt, sollte normalerweise auch die Verantwortung für die Freigabe übernehmen.
Eine Klasse, die in ihrem Konstruktor Unterobjektte erzeugt sollte dann die Verantwortung für ihre Freigabe im Destruktor übernehmen.

Die Anwendung von FreeAndNil() ist häufig ein Kennzeichen dafür, dass die Verantwortlichkeiten nicht sauber geregelt sind.
Delphi-Quellcode:
TMyBox = class(TWorldBox)
  public
    { Public-Deklarationen }
    Parameter      : TParmeter;
    destructor Destroy; override;
   end;

destructor TMyBox.Destroy;
begin
  FreeAndNil(Parameter); // Falsch! 
  // die Variable Parameter wurde ganz offensichtlich von einer anderen Klasse erstellt
  // TMyBox trägt nicht die Verantwortung für die Freigabe
  // FreeAndNil() wäre sowieso sinnlos, da die Variable Parameter demnächst "out of scope" geht

  inherited Destroy;
end;
Also nicht blind mit FreeAndNil() um sich schiessen, sondern genau überlegen.

SteffenSchm 26. Feb 2009 09:35

Re: Objekte freigeben
 
Vielen Dank für die vielen Hinweise und die interessante Diskussion. Noch ein paar Erläuterungen:

Die Klasse TMyBox stellt Daten in Abhängigkeit von Parametern da. Das Objekt Parameter wird in dieser Klasse im constructor erzeugt und auch im destructor wieder freigegeben. (@sx2008:Das FreeAndNil an dieser Stelle ist tatsächlich nutzlos. Ich habe es deshalb wieder zurückgenommen.) Die Klasse sieht jetzt so aus:

Delphi-Quellcode:
TMyBox = class(TWorldBox)
  public
    { Public-Deklarationen } 
    Parameter      : TParmeter;
    construcor Create(AOwner: TComponent); override;
    destructor Destroy; override;
   end;

constructor TMyBox.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  Parameter     :=TParameter.Create;
end;

destructor TMyBox.Destroy;
begin
  Parameter.Free;
  inherited Destroy;
end;
Diese Klasse nutze ich für verschiedene Projekte. Ich will sie deshalb für das aktuelle Problem nichr grundlegend ändern. Im aktuellen Projekt soll es aber möglich sein, zwischen drei unterschiedlichen Darstellungen umzuschalten. Die Art der drei Darstellungen ist in drei Objekten TParameter gespeichert. Den aktuell zu verwendenden weise ich dann MyBox.Parameter zu. Das grundlegende Problem war, dass ich nach Freigabe von MyBox nicht wusste, welcher der drei Parameterobjekte bereits freigegeben ist.

Ich habe mich jetzt dazu entschlossen das aufrufende Objekt von TMyBox abzuleiten. Das ganze sieht jetzt so aus:

Delphi-Quellcode:
  TArea = class(TMyBox)
  public
    ParameterA,
    ParameterB,
    ParameterC: TParameter;
    construcor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

constructor TArea.Create(AOwner: TComponent);
begin
  inherited Create(MainForm);
  Parent  := MainForm.TabControl;
 
  ParameterA := Parameter;
  ParameterB := TParameter.Create;
  Parameterc := TParameter.Create;
 end;

destructor TCalcArea.Destroy;
begin
  FreeAndNil(ParameterA);
  FreeAndNil(ParameterB);
  FreeAndNil(ParameterC);
  Parameter:=Nil;
  inherited Destroy;
end;
Ich habe damit immer drei erzeugte TParameter-Objekte aber vier Zeiger die darauf zeigen. Im destructor von TArea gebe ich aller drei erzeugten Parameter frei. Den vierten Zeiger muss ich aber zusätzlich auf Nil setzen, ansonsten würde Parameter.Free im destructor von TMyBox zum Fehler führen.

Das ganze scheint jetzt so zu funktionieren. Für Hinweise und Anregungen bin ich dennoch dankbar !

shmia 26. Feb 2009 17:49

Re: Objekte freigeben
 
Diese Deklaration ist nicht sicher:
Delphi-Quellcode:
TMyBox = class(TWorldBox)
 public
   { Public-Deklarationen } 
   Parameter      : TParameter;
   construcor Create(AOwner: TComponent); override;
   destructor Destroy; override;
end;
Das Problem ist, dass jeder von Aussen die Variable Parameter umbiegen kann.
Das führt zwangsläufig zu Fehlern.

Über ein Property kann man die Sache aber wasserdicht machen:
Delphi-Quellcode:
TMyBox = class(TWorldBox)
 private
   FParameter : TParameter;
   procedure SetParameter(value:TParameter);

 public
   { Public-Deklarationen } 
   construcor Create(AOwner: TComponent); override;
   destructor Destroy; override;
   property Parameter : TParameter read FParameter write SetParameter;
end;

procedure TMyBox.SetParameter(value:TParameter);
begin
  FParameter.Assign(value);
end;
Die Klasse TParameter muss von TPersistent abgeleitet werden und die Methode Assign muss überschrieben werden.
Mehr zu Assign() - leider auf englisch http://delphi.about.com/od/course/a/delphi_oop21.htm

sirius 26. Feb 2009 19:57

Re: Objekte freigeben
 
Anstatt Assign besser AssignTo überschreiben. aber vielleicht braucht er ja keinen Setter. Der gezeigte Code oben ist "schlecht", aber für Verbesserungen braucht man hier mehr Informationen.

SteffenSchm 27. Feb 2009 07:27

Re: Objekte freigeben
 
Danke für die Hinweise.

Wenn ich das richtig verstehe, wird mit Assign (oder AssignTo) dann der Inhalt des Objektes auf ein neues Objekt kopiert. Ich brauche dann tatsächlich vier erzeugte und auch wieder freizugebende Objekte vom Typ TParameter. Dann ist das mit dem Freigeben natürlich klar geregelt und erzeugt keine Konflikte.

Ich hatte versucht, nur einen Zeiger auf das aktuelle Objekt Parameter zu setzen (bzw. umzubiegen). Ich dachte das wäre effizienter. Aber es ist wohl nicht ganz sauber programmiert.

Noch eine Frage: Wenn ich den Parameter in MyBox über ein property anspreche, warum kann ich dann nicht in der Methode SetParameter den Code reinschreiben, den ich in die Methode Assign bzw. AssignTo der von TPersistent abgeleiteten Klasse TParameter schreiben soll. Wäre zumindest für mich dann einfacher zu lesen und zu verstehen.

SteffenSchm 27. Feb 2009 16:17

Re: Objekte freigeben
 
Das ganze wirft bei mir jetzt eine prinzipielle Frage auf.

Ich "missbrauche" Ableitungen von TObject auch an anderen Stellen noch als Zeiger. Am besten vielleicht an einem Beispiel zu erklären:

Ich habe eine Datenstruktur ähnlich einer Master/Detail-Tabelle in einer Datenbank. Die Mastertabelle (bzw. -liste) ist eine Objektliste mit z.B. Werkstücken. Sie enthält Verweise auf eine Detailtabelle (auch hier wieder eine Objektliste) in der der Werkstoff des Werkstücks steht.

Für die Werkstoffe habe ich ein Objekt vom Typ TMat = class(TObject) definiert. Diese Objekte werden erzeugt, in der Liste gespeichert und am Ende auch wieder freigegeben.

Innerhalb des Objektes Werkstück benutze ich auch TMat, hier aber nur als Zeiger auf ein Element der Materialliste. Es wird vom Objekt Werkstück weder erzeugt noch freigegeben.

Ist das aus OOP-Gesichtspunkten so o.k., oder sollte man das prinzipiell anders machen.

Vielleicht schaut jemand mit Ahnung noch mal in dieses Thema rein und hat eine Anmerkung bzw. einen Hinweis. Besten Dank!

sx2008 28. Feb 2009 10:35

Re: Objekte freigeben
 
Zitat:

Zitat von SteffenSchm
Noch eine Frage: Wenn ich den Parameter in MyBox über ein property anspreche, warum kann ich dann nicht in der Methode SetParameter den Code reinschreiben, den ich in die Methode Assign bzw. AssignTo der von TPersistent abgeleiteten Klasse TParameter schreiben soll. Wäre zumindest für mich dann einfacher zu lesen und zu verstehen.

Es ist besser, eine Klasse die Möglichkeit zu geben, Objekte zu kopieren, als dass man diese Aufgabe in sehr begrenzter Form einer anderen Klasse überlässt.
Die Klasse TParameter weiss selbst am Besten, wie sich sich kopieren soll.

Assign() oder AssignTo():
Wenn eine Klasse Objekte von sich selbst kopieren soll, dann verwendet man immer Assign().
AssignTo() kommt nur in ganz bestimmten Sonderfällen zum Einsatz.
Angenommen, man hat eine neue Klasse namens TSuperBitmap geschrieben.
Dann möchte man sicher die SuperBitmap auch auf ein normales TBitmap-Objekt kopieren:
Delphi-Quellcode:
bitmap.Assign(superbitmap);
Jetzt ergibt sich nur das Problem, dass die Klasse TBitmap unsere neue Klasse TSuperbitmap nicht kennt.
Und man kommt auch an TBitmap.Assign() nicht heran, weil der Code in der VCL steckt.
Aber man kann die Sache umdrehen und TSuperBitmap.AssignTo() überschreiben und so das Dilemma lösen.

schöni 28. Feb 2009 11:32

Re: Objekte freigeben
 
Hallo,

Keine Ahnung, ob es im konkreten Fall hilft, aber ich gebe Objekte grundsätzlich nach Stack-Prinzip frei. In umgekehrter Reihenfolge der Initialisierung:

Delphi-Quellcode:
var
 Object1: TMyFirstClass;
 Object2: TMySecondClass;
 Object3: TMyThirdClass;

begin
  Object1 := TMyFirstClass.Create;
  Object2 := TMySecondClass.Create;
  Object3 := TMyThierClass.Create;
  ...
  if Assigned(Object3) then FreeAndNil(Object3);
  if Assigned(Object2) then FreeAndNil(Object2);
  if Assigned(Object1) then FreeAndNil(Object1);
end.


Alle Zeitangaben in WEZ +1. Es ist jetzt 09:43 Uhr.
Seite 2 von 3     12 3      

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