Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Free eines unbekannten Objektes (https://www.delphipraxis.net/120404-free-eines-unbekannten-objektes.html)

olee 10. Sep 2008 17:06


Free eines unbekannten Objektes
 
Hi,

Ich habe eine TList mit den Pointern zu Objekten von unterschiedlichem Typ
jedoch stammen alle diese von einem Grundtyp ab < XXX = class (T3dObj) >.

Nun ist mein Problem, dass ich diese Objekte auch wieder richtig freigeben muss.

Zuerst hatte ich T3DObj(ListemitObjekten.Items[i]).Free;

Doch anscheinend funktioniert das nicht so recht.

Hat jemand ne Idee, wie ich den Speicher wieder freigeben kann?

Oder wenn möglich die größe eines Objektes an einem Pointer ermitteln kann?


MFG
Björn

sirius 10. Sep 2008 17:18

Re: Free eines unbekannten Objektes
 
Genau das sollte funktionieren. Dafür ist ja Veerbung und Polymorphie da.
btw: Die Methode free ist von TObject. Du kannst also auch auf TObject casten und free aufrufen.

Probier vielleicht mal das casten mit as:
Delphi-Quellcode:
(ListemitObjekten.Items[i] as T3DObj).Free;
//oder
[s](ListemitObjekten.Items[i] as TObject).Free; [/s]
Das könnte evtl. die Fehlersuche erleichtern.


Edit: As TObject ist natürlich quatsch, sorry.
Dann doch eher:
Delphi-Quellcode:
TObject(ListemitObjekten.Items[i]).Free;
//oder alles zusammen
(TObject(ListemitObjekten.Items[i]) as T3DObj).Free;
Und was heißt: "es funktioniert nicht"?

rollstuhlfahrer 10. Sep 2008 17:24

Re: Free eines unbekannten Objektes
 
HI,

solange du nicht die Prozedur Free überschrieben hast, sondern immer nur schön den DESTRUCTOR-Teil sollte es mit .Free ohne Probleme gehen. Du kannst ja mal einen Testlauf machen, bei dem du nur Objekte vom gleichen Typ in die Liste legst und dann jedes Element mit FreeAndNil() aus dem Speicher fegst. Falls du hier merkst, das da ein paar Code-Zeilen nicht ausgeführt werden, dann solltest du nochmals überprüfen, was du mit .Free gemacht hast. (Dieser Fehler tritt des öfteren mal auf.) Ansonsten kannst du den Pointer auch direkt nach FreeAndNil übergeben, da dieser dort nach TObject gecastet wird.

Bernhard

Bernhard Geyer 10. Sep 2008 17:30

Re: Free eines unbekannten Objektes
 
Zitat:

Zitat von olee
Doch anscheinend funktioniert das nicht so recht.

Wie wirkt sich das aus bzw. erkennst du das dies nicht korrekt funtionieren würde?

olee 10. Sep 2008 17:54

Re: Free eines unbekannten Objektes
 
Also das ist so:

Delphi-Quellcode:
// Die Objekte
T3DCollideObj= class(T3DObj)
T3DMoveObj= class(T3DCollideObj)
T3DBlock = class(T3DMoveObj)

// das entfernen der Objekte:
  For i := 0 To Objects.Count - 1 Do
    T3DObj(Objects[i]).Free;


//Die Stelle wo ich den Fehler bemerkt hab:

WorldActor : T3DBlock;

// wenn ich hier "Auswerten / Ändern" auf WorldActor anwende, ist das nicht nil,
// aber es wurde eindeutig etwas daran geändert, weil
// 1.) WorldActor.Free; zu einem Fehler führt
// 2.) Variablen von WorldActor danach zufallswerte haben

//  If T3DObj(WorldActor) <> nil then // die kann weg (ist mir grad eingefallen)
    WorldActor.Free;
  WorldActor := T3DBlock.Create;
EDIT: WorldActor ist nur einesunter vielen Objekten, das verwendet wird.
Genau genommen gibts WorldActor nur einmal

sirius 10. Sep 2008 19:19

Re: Free eines unbekannten Objektes
 
Ja, free löscht ja nicht die Referenzen auf das Objekt sondern nur das Objekt.
In Worldactor steht immer noch ein Pointer. Der zeigt zwar ins Nirvana (aber nicht nach nil) und zwar genau dorthin, wo früher mal ein Objekt war (ist wie, wenn in einer Landkarte Troja eingezeichnet ist). Aber das Objekt gibt es deswegen nicht.

Wenn du dieses Problem hast, dann musst du konsequent nach dem Aufruf von Free die Reerenz (oder die Referenzen) löschen (per Hand auf nil setzen.
Und da du nicht der erste bist, der dieses Problem hat, gibt es die Funktion FreeandNil. Die macht auch nix anderes. Aber du kannst dir ja angewöhnen anstatt MyObject.Free leber freeandnil(Myobject) aufzurufen.

Roachford 10. Sep 2008 19:27

Re: Free eines unbekannten Objektes
 
Zitat:

Zitat von sirius
Edit: As TObject ist natürlich quatsch, sorry.

Warum sollte? Begründung bitte?

Das einzige was an deinem Code nicht so 1:1 klappt ist, dass der Threadersteller geschrieben hatte, TList zu verwenden. Somit gibt die Liste Pointer zurück und da verweigert der Compiler die Anwendung von IS und AS, aber ansonsten ist der Code in Ordnung (auch mit TObject, von daher die Frage).

Und statt TList ist vllt. eher zu überlegen TObjectList zu benutzen. Diese gibt die Objekte gleich frei beim entfernen und genauso beim Überschreiben. Also wenn ein Eintrag X mit einer anderen Instanz zugewiesen wird, dann wird die originale Instanz freigegeben und die neue and der Position gesetzt.

sirius 10. Sep 2008 19:42

Re: Free eines unbekannten Objektes
 
Zitat:

Zitat von Roachford
Warum sollte? Begründung bitte?

Du hast die Frage ja fast selber beantwortet.
  1. Den As-Operator kann ich nur auf Objekte anwenden.
  2. Alle Objekte sind von TObject abgeleitet.
  3. Also haben alle Objekte die Methoden und Eigenschaften von TObject.
  4. As TObject verkümmert immer zu einem simplen TypeCast.
  5. Ein As Operator auf eine Vorfahrklasse ist nicht notwendig und wird daher auch vom Compiler ignoriert
  6. As ist nur notwendig, wenn ich auf bestimmte Nachfahren einer Klasse testen/casten möchte.
Man würde ja auch nicht von einem Pointer (p) ausgehend folgendes schreiben
Delphi-Quellcode:
TObject(p) as TObject
wohl aber:
Delphi-Quellcode:
TObject(p) as TComponent

Anders ausgedrückt: Es gibt keine Möglichkeit (außer gewisse Plausibilitätstests) zu überprüfen, ob ein pointer auf ein Objekt zeigt. Und genau das müsste ja "As TObject" machen. Wenn es aber bereits ein Objekt ist, dann brauche ich auf TObject nicht zu testen (is) oder zu Konvertieren (as). Dann ist die Frage (As) ja bereits die Bedingung.


btw.: "is TObject" testet übrgiens nur auf <>nil.

automatix 10. Sep 2008 19:47

Re: Free eines unbekannten Objektes
 
Hallo!

Statt einer TList besser eine TObjectList nehmen und OwnObjects aus True. Dann kann man sich auch die Schleife sparen sondern einfach FreeAndNil(Objects) aufrufen und alle in der Liste enthaltenen Objekte werden auch freigegeben.

Bei einer TList müsste es da nicht heißen?
Delphi-Quellcode:
  T3DObj(Objects[i]^).Free;
Grüße

olee 10. Sep 2008 20:17

Re: Free eines unbekannten Objektes
 
Zitat:

Zitat von automatix
Hallo!
Bei einer TList müsste es da nicht heißen?
Delphi-Quellcode:
  T3DObj(Objects[i]^).Free;

Nein das nicht, da TList nur Pointer speichert.
Objects[i]^ ist nur möglich, wenn Objects[i] : ^T3dObj oder so wäre.

Das merkwürdige an der ganzen sache ist aber noch, dass selbst wenn ich
Delphi-Quellcode:
  T3DObj(WorldActor).free;
  If T3DObj(WorldActor) <> nil then [...]
benutze gibt "T3DObj(WorldActor) <> nil" true zurück.

sirius 10. Sep 2008 20:24

Re: Free eines unbekannten Objektes
 
Zitat:

Zitat von olee
Das merkwürdige an der ganzen sache ist aber noch, dass selbst wenn ich
Delphi-Quellcode:
  T3DObj(WorldActor).free;
  If T3DObj(WorldActor) <> nil then [...]
benutze gibt "T3DObj(WorldActor) <> nil" true zurück.

Das ist nicht merkwürdig, das ist nur logisch und auch nicht anders machbar, siehe Post #6.

olee 10. Sep 2008 20:36

Re: Free eines unbekannten Objektes
 
ja aber selbst als ich das hier verwendet habe:
Delphi-Quellcode:
  For i := 0 To Objects.Count - 1 Do
  begin
    T3DObj(Objects[i]).Free;
    Objects[i] := nil;
  end;
aber da fällt mir ein:

Man muss bedenken Objects[i] ist nicht WorldActor aber hat den gleichen wert

olee 10. Sep 2008 20:47

Re: Free eines unbekannten Objektes
 
Ma ne frage zur TObjectList:

Der gibt mit ObjectList.Items[X] ein TObject zurück.

Muss ich das einfach casten?

DeddyH 10. Sep 2008 20:57

Re: Free eines unbekannten Objektes
 
Sicher.

automatix 11. Sep 2008 09:19

Re: Free eines unbekannten Objektes
 
Hallo!

Dies funktioniert, wie schon geschrieben, nicht.

Delphi-Quellcode:
  TKlasse1(list[i]^).Free;
Ich habe es bisher fast immer vermeiden können in Delphi mit Pointern zu arbeiten und muss daher i.d.R. immer erst probieren, wann eine Pointer Dereferenzierung mittel ^ nötig ist und wann nicht.

Ich habe hier mal ein paar Möglichkeiten runtergetippt.

Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Contnrs,
  Forms, Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

  TKlasse1 = class(TObject)
  public
     constructor Create;
    destructor Destroy; override;
  end;

  TKlasse2 = class(TKlasse1)
  public
     constructor Create;
    destructor Destroy; override;
  end;

  TKlasse3 = class(TKlasse2)
  public
     constructor Create;
    destructor Destroy; override;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

const
   MAX_OBJ = 5;

procedure TForm1.FormCreate(Sender: TObject);
begin
   Randomize;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
   i: Integer;
  list: TList;
begin
   list := nil;
  try
     list := TList.Create;

    for i := 0 to Pred(MAX_OBJ) do begin
      case Random(3) of
      0: list.Add(TKlasse1.Create);
      1: list.Add(TKlasse2.Create);
      2: list.Add(TKlasse3.Create);
      end;
    end;

    // ...

    for i := 0 to Pred(list.Count) do
      TKlasse1(list[i]).Free;
      //(list[i] as TKlasse1).Free;              // Operator ist auf diesen Operandentyp nicht anwendbar
      //(TObject(list[i]) as TKlasse1).Free; // :-)

    MessageDlg('Fertig mit Free', mtInformation, [mbOk], 0);
  finally
    FreeAndNil(list);
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
   i: Integer;
  list: TObjectList;
begin
   list := nil;
  try
     list := TObjectList.Create;

    for i := 0 to Pred(MAX_OBJ) do begin
      case Random(3) of
      0: list.Add(TKlasse1.Create);
      1: list.Add(TKlasse2.Create);
      2: list.Add(TKlasse3.Create);
      end;
    end;

    // ...

    for i := 0 to Pred(list.Count) do
      TKlasse1(list[i]).Free;                  // Hier sind beide Varianten möglich
      // (list[i] as TKlasse1).Free;

    MessageDlg('Fertig mit Free', mtInformation, [mbOk], 0);
  finally
     // Hier knallt es, weil durch OwnsObjects = True der Destruktor der Liste
    // versucht die schon freigegebenen Objekte noch einmal freizugeben
    FreeAndNil(list);
  end;
end;

procedure TForm1.Button3Click(Sender: TObject);
var
   i: Integer;
  list: TObjectList;
begin
   list := nil;
  try
     list := TObjectList.Create;
    list.OwnsObjects := False;

    for i := 0 to Pred(MAX_OBJ) do begin
      case Random(3) of
      0: list.Add(TKlasse1.Create);
      1: list.Add(TKlasse2.Create);
      2: list.Add(TKlasse3.Create);
      end;
    end;

    // ...

    for i := 0 to Pred(list.Count) do
      TKlasse1(list[i]).Free;                  // Hier sind beide Varianten möglich
      // (list[i] as TKlasse1).Free;

    MessageDlg('Fertig mit Free', mtInformation, [mbOk], 0);
  finally
    FreeAndNil(list);
  end;
end;

procedure TForm1.Button4Click(Sender: TObject);
var
   i: Integer;
  list: TObjectList;
begin
   list := nil;
  try
     list := TObjectList.Create;

    for i := 0 to Pred(MAX_OBJ) do begin
      case Random(3) of
      0: list.Add(TKlasse1.Create);
      1: list.Add(TKlasse2.Create);
      2: list.Add(TKlasse3.Create);
      end;
    end;

    // ...
  finally
    FreeAndNil(list);
  end;
end;

{ TKlasse1 }

constructor TKlasse1.Create;
begin
   inherited Create;
end;

destructor TKlasse1.Destroy;
begin
   MessageDlg('Destruktor Klasse 1', mtInformation, [mbOk], 0);
  inherited Destroy;
end;

{ TKlasse2 }

constructor TKlasse2.Create;
begin
   inherited Create;
end;

destructor TKlasse2.Destroy;
begin
   MessageDlg('Destruktor Klasse 2', mtInformation, [mbOk], 0);
  inherited Destroy;
end;

{ TKlasse3 }

constructor TKlasse3.Create;
begin
   inherited Create;
end;

destructor TKlasse3.Destroy;
begin
   MessageDlg('Destruktor Klasse 3', mtInformation, [mbOk], 0);
  inherited Destroy;
end;

end.

sirius 11. Sep 2008 09:24

Re: Free eines unbekannten Objektes
 
Zitat:

Zitat von olee
ja aber selbst als ich das hier verwendet habe:
Delphi-Quellcode:
  For i := 0 To Objects.Count - 1 Do
  begin
    T3DObj(Objects[i]).Free;
    Objects[i] := nil;
  end;
aber da fällt mir ein:

Man muss bedenken Objects[i] ist nicht WorldActor aber hat den gleichen wert

Du hast also zwei Referenzen auf ein Objekt. Wie gesagt, die Referenzen musst du per Hand auf nil testen. Aber du solltest eher etwas in deiner Logik verändern. Es geht prinzipiell nicht so wie du möchtest.

Sherlock 11. Sep 2008 09:26

Re: Free eines unbekannten Objektes
 
Vielleicht hab ich Deine Antwort überlesen, aber warum stellst Du nicht auf TObjectList um? Mit der Property OwnsObjects auf True gibt die automatisch alle angehängten Objekte selber frei sobald sie freigegeben wird.

Sherlock

olee 11. Sep 2008 13:37

Re: Free eines unbekannten Objektes
 
Ja das freigeben war ja nicht so richtig das Problem, wie sich herausgestellt hat.

Ich verwende jetzt eine TObjectList,

und das Problem habe ich so gelöst:

Delphi-Quellcode:
  If BEngine.Objects.IndexOf(WorldActor) >= 0 Then
    BEngine.Objects.Delete(BEngine.Objects.IndexOf(WorldActor));
Anstatt
Delphi-Quellcode:
If T3DObj(WorldActor) <> nil then T3DBlock(WorldActor).free;
Vielen dank für eure hilfe!
Und vor allem an Sirius, für die Hilfe bei meinem letzten Problem ^^

Das Ergebnis meiner Arbeit kann jetzt hier betrachtet werden.


Alle Zeitangaben in WEZ +1. Es ist jetzt 22:02 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