Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi TList mit "verschiedenen Pointern" freigeben (https://www.delphipraxis.net/90813-tlist-mit-verschiedenen-pointern-freigeben.html)

Mattze 24. Apr 2007 10:44


TList mit "verschiedenen Pointern" freigeben
 
Hallo,

ist mir gerade so aufgefallen...
Ich habe eine Tlist und möchte die einmal mit PRecord1 und einmal mit PRecord2 füllen.
Am Programmende (und nicht nur dort) gebe ich die items natürlich frei. Nur, ich weiß da nicht, welcher Precord da gerade drin ist. Das gibt beim Freigeben Probleme, da ich natürlich nicht dispose(PRecord1(Liste[i]) für Pointer vom Typ precord2 machen kann.
Hat jemand einen Tipp für mich, wie man das am günstigsten löst? (Bitte nicht "mit 2 TList"!)

Gruß
Matthias

sirius 24. Apr 2007 10:54

Re: TList mit "verschiedenen Pointern" freigeben
 
Wieso nicht einfach dispose(Liste[i])?

chaosben 24. Apr 2007 10:54

Re: TList mit "verschiedenen Pointern" freigeben
 
Ich über deine Daten (PRecord1, PRecord2) noch ein Record setzen in dem du dir den Typ merkst.

Muetze1 24. Apr 2007 11:03

Re: TList mit "verschiedenen Pointern" freigeben
 
Du hast eine Liste wo du nicht weisst ob Typ 1 oder Typ 2 drin enthalten ist? Da frage ich mich doch: wie arbeitest du denn mit der Liste, wenn du nirgendwo weisst, welcher Typ an welchem Index steht?

Mattze 24. Apr 2007 13:42

Re: TList mit "verschiedenen Pointern" freigeben
 
Hallo,

dispose(Liste[i]) dachte ich auch erst. Ist ja eigentlich auch das Naheliegende. Aber da stürzt er ab.
Bisher habe ich nur gefunden, dass es an den unterschiedlichen Sätzen liegt.
Wundert mich zwar auch, aber was soll man machen...

Ich habe nicht gesagt, dass ich nicht weiß, was in der Liste steht.
Du hast mich aber wahrscheinlich nicht richtig verstanden. In der GANZEN Liste stehen nur Sätze vom Typ1 oder Typ2 - also nur entweder oder für alles! Die Items unterscheiden sich nicht von ihrer inhaltlichen Struktur! Da ich das Programm aber jederzeit beenden kann und die Liste erst unmittelbar vor der nächsten Verwendung lösche - und dann kommt da eben Typ1 oder Typ2 rein - weiß ich beim Beenden nicht, was da gerade drin ist.

An einen "übergeordneten Satz", der auch noch den Typ enthält, habe ich auch schon gedacht. Hätte den Vorteil, dass man die Liste gleichzeitig mit völlig unterschiedlichen Strukturen füllen kann. Aber das ist eigentlich nicht nötig - für jeden Satz.
Vielleicht wäre eine (globale) Variable, die man beim Befüllen der Liste einmal setzt, genug...

Mal sehen...

Gruß
Matthias

sirius 24. Apr 2007 16:44

Re: TList mit "verschiedenen Pointern" freigeben
 
Bei mir stürzt er nicht ab. Warum auch? Dispose bzw Freemem bekommt den Pointer wo dein record liegt und genau davor liegt ein Integer-Wert, der die Größe deines Records beinhaltet. Damit ist beiden Funktionen völlig egal, was für einen Typ du da hast.

Jelly 24. Apr 2007 16:48

Re: TList mit "verschiedenen Pointern" freigeben
 
Nimm eine TObjectList anstatt einer TList. Die gibt ihre Elemente automatisch frei, sobald du die Liste frei gibst.

DGL-luke 24. Apr 2007 16:57

Re: TList mit "verschiedenen Pointern" freigeben
 
... aber keine dynamisch alloziierten records.

Muetze1 24. Apr 2007 23:10

Re: TList mit "verschiedenen Pointern" freigeben
 
...und die Records zu Objekten (was Jelly zwangsläufig mit meinte) machen? Die kann man schliesslich genauso dynamisch alloziieren...

DGL-luke 25. Apr 2007 16:11

Re: TList mit "verschiedenen Pointern" freigeben
 
... geht aber an der TF völlig vorbei (siehe Topic)...

:stupid: :mrgreen:

SirThornberry 25. Apr 2007 16:39

Re: TList mit "verschiedenen Pointern" freigeben
 
wie bereits erwähnt könnten beide Recordtypen am Anfang des Records ein Feld haben welches den Typ angibt. Mit einem Cast auf einen der beiden Recordtypen kannst du dann prüfen welcher Typ tatsächlich in der Liste hängt.

Zum Vorschlag einfach
Delphi-Quellcode:
dispose(liste.Items[i])
zu verwenden:
Generell sollte das funktionieren. Allerdings habe ich gelesen (weiß nicht obs stimmt) das bei Records in denn Strings etc. enthalten sind von denen der Speicher nicht frei gegeben wird.

[Edit]
ich habs inzwischen mal "getestet".
Ohne Cast auf den Typ (dispose(liste.Items[i])) wird intern FreeMem aufgerufen
Mit Cast auf den Typ (dispose(PRecordTyp(liste.Items[i]))) wird intern Dispose aufgerufen

und dispose ruft intern Finalize auf was wiederum FinalizeArray aufruft was wohl dynamische Arrays und Strings frei gibt.
[/Edit]

3_of_8 25. Apr 2007 17:08

Re: TList mit "verschiedenen Pointern" freigeben
 
Wie kann denn FreeMem aufgerufen werden, wenn Dispose gar nicht "weiß", wie viel Speicher freigegeben werden darf?

sirius 25. Apr 2007 17:59

Re: TList mit "verschiedenen Pointern" freigeben
 
@Sir: Habs auch getestet und kann all deine Vermutungen bestätigen.

@3_of_8
Wenn der Compiler bei dispose einen untypisierten pointer (also list.items[i] : pointer) erkennt ruft er automatisch nur freemem auf. Vor jedem alloziierten Speicherplatz steht desen Größe (und noch ein bisschen mehr) Wenn du also mit Getmem 10 Bytes alloziierst, dann steht bei addr(list.items[i]-4) eine 10, wenn du einen string hast (z.B. s:='Hallo'), dann steht dort (bei addr(@s-4)eine 5), somit kann freemem, ohne Größenangabe speicher freigeben, denn die Größenangabe steht ja vor dem Datentyp (egal welcher, für arrays gilt dasgleiche)
Dort steht allerdings weder ne 10 noch ne 5. sondern an der Stelle sind mehrere Werte versteckt, aber mann kann es eineindeutig zurücktransformieren (zu 10 und 5)

Der angesprochene Unterschied, den Sir meinte, ist wenn ich dispose(PRecord1(list.items[i])) mache, dann ruft der Compiler auch tatsächlich dispose auf. Und Dispose ruft intern das angesprochene Finalize (etc. auf) und hinterher freemem. also ist der Unterschied nur, in dem finalize. Und ich habe gerade überprüft: Finalize gibt tatsächlch den Speicher von Arrays, strings, etc frei.
Nun, woher weis finalize, wo die strings im Record liegen? Versteckt übergibt der compiler bei dispose noch einen zweiten Parameter. Dies ist eine Adresse im Datensegment und beinhaltet die Struktur des Records. Und jetzt schließt sich der Kreis. Damit weis finalize, was und wo es noch strings, arrays, sonstige dynamische Variablen freigeben muss. Und zum zweiten ist es jetzt auch klar, warum wir den Typecast auf PRecord1 brauchen: Der Compiler muss ja auch wissen, welchen zweiten Parameter er an dispose übergeben muss.

Zusammenfassung:
aus dispose(list.items[i]) wird freemem(list.items[i])
aus dispose(PRecord1(list.items[i])) wird dispose(list.items[i],addr(Precord1Structure))


So, und jetzt noch eine neue Lösung für oben genanntes Problem:
Delphi-Quellcode:
unit Unit1;

interface

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

type Pp1=^Rp1;
     Rp1=record
       //typ ist für die wiedererkennung des Records
       typ:byte;
       //ab hier können jetzt statische und/oder dynamische Variablen folgen
       s:string;
     
     end;
type Pp2=^Rp2;
     Rp2=record
       //wir das obige Record
       typ:byte;
       s:array of integer;
       x:byte;
     end;
type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure fill;
    procedure clear;
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
    list:Tlist;
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}


procedure Tform1.fill;
var p1:Pp1;
    p2:Pp2;
begin
  list:=Tlist.create;
  new(p1);
  p1^.typ:=1; //Typ zum wiedererkennen festlegen
  p1^.s:='Hallo';

  new(p2);
  p2^.typ:=2;
  setlength(p2^.s,2);
  p2^.s[0]:=10;
  p2^.s[1]:=20;

  list.add(p2);
  list.add(p1);

  new(p1);
  p1^.typ:=1;
  p1^.s:='';
  list.add(p1);

end;
procedure Tform1.clear;
var i:integer;
    typ:pbyte;
begin
  for i:=0 to list.Count-1 do begin
    typ:=list[i];
    case typ^of
      1: dispose(Pp1(list[i]));
      2: dispose(Pp2(list[i]));
      else raise Exception.CreateFmt('$d ist kein gültiger Recordtyp!',[typ^]);
    end;
  end;
  list.free;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  fill;
  clear;
end;

end.
Was bei allen tollen Funktionen auch der Compiler nicht schaffen wird, sind speicherbereiche freigeben, auf die ein untypisierter Pointer innerhalb des records zeigt

Mattze 29. Mai 2007 17:32

Re: TList mit "verschiedenen Pointern" freigeben
 
Hallo,

'tschuldigung. Ging jetzt eine Weile nicht bei mir!
Aber jetzt!
Vielen Dank für Eure Hilfe.
Inzwischen klappt es und natürlich klappt es so, wie es ja auch klappen muss - mit dispose.
Es war ein echt dummer gedanklicher Fehler von mir. Ich habe "die Reichweite von Assign" unlogisch hoch angesetzt.

Gruß
Mattze


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