Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Was macht Free genau in einem Array (https://www.delphipraxis.net/58531-macht-free-genau-einem-array.html)

s.h.a.r.k 8. Dez 2005 15:40


Was macht Free genau in einem Array
 
Abend :wink:

Hab da grad ein Problem - denke wohl eher ein Verständnis-Problem: Und zwar habe ich ein variables Array, das von einem eigenen Typ ist, nehmen wir mal an von Typ TFont. Nun habe ich eine Procedure Add, mit der ich dem Array von mir aus 10 Einträge hinzufüge. Danach will ich den letzten Eintrag löschen und habe bisher das so gemacht:

Delphi-Quellcode:
MyArray[9].Free;
setlength(MyArray, length(MyArray) - 1);
Mein Gedanke: Mit Free gebe ich den belegten Speicher wieder frei und mit dem setlength verkürze ich das Array. Allerdings bringt mir Delphi bei einer späteren Verwendung des Array ein EA-Fehler, was ich irgendwie nicht ganz versteh: Den das Free gibt auch irgendwie das Array-Feld frei - nur warum?!?! Stimmt meine letzte Annahme überhaupt???

mfg shark

ichbins 8. Dez 2005 16:00

Re: Was macht Free genau in einem Array
 
:gruebel:

Das free gibt nur die Font-Variable frei, die in dem Speicherbereich gespeichert ist. Bei mir läuft alles Glatt:

Delphi-Quellcode:
var
  a:array of tfont;
  i:integer;
begin
  setlength(a,10);
  for i:=0 to length(a)-1 do
    a[i]:=tfont.create;
  a[length(a)-1].Free;
  setlength(a,length(a)-1);
  a[5].Size:=10;
  a[8].Name:='Hallo';
end;
Es kommt keine Exception.

Könnte es vllt sein dass du nochmal auf Element [9] zugreifst?

Der_Unwissende 8. Dez 2005 16:01

Re: Was macht Free genau in einem Array
 
Zitat:

Zitat von s.h.a.r.k
Mein Gedanke: Mit Free gebe ich den belegten Speicher wieder frei und mit dem setlength verkürze ich das Array. Allerdings bringt mir Delphi bei einer späteren Verwendung des Array ein EA-Fehler, was ich irgendwie nicht ganz versteh: Den das Free gibt auch irgendwie das Array-Feld frei - nur warum?!?! Stimmt meine letzte Annahme überhaupt???

Hi,
also mit dem Free gibst du definitiv nicht das komplette Array frei, sondern machst genau das was du möchtest (vorrausgesetzt ich hab dich richtig verstanden). Wenn du ein TFont aFont anlegst und an der Stelle 10 ins Array schreibst, dann wird eben aFont freigegeben.
Wird die Referenz noch irgendwo im Programm benutzt, so ist diese natürlich genauso freigegeben worden. Wenn du also nach einem solchen Free ein Problem hast, solltest du nochmal kontrollieren, ob du noch irgendwo anders auf das gleiche Element verweist und natürlich auch, dass du nicht über die Grenzen des Arrays hinaus gehst (nach Verminderung).

Gruß Der Unwissende

s.h.a.r.k 8. Dez 2005 16:08

Re: Was macht Free genau in einem Array
 
Vielleicht habe ich mich etwas kompliziert ausgedrückt: Und zwar wie schon gesagt habe ich ein Array der Länge 10 und lösche nun z.B. das Fünfte über eine Procedure Delete, die wie folgt aussieht:

Delphi-Quellcode:
procedure TMyClass.Delete(Index: Integer);
var
  i : Integer;
begin
  MyArray[Index].Free;
  for i := Index to length(MyArray) - 2 do MyArray[i] := MyArray[i + 1];
  setlength(MyArray, length(MyArray) - 1);
end;
Nun ist es allerdings so, dass das Free schon eine Dekrementierung des Arrays bewirkt, was ich allerdings nicht wirklich verstehe...

PS: Sicherheitsabfragen bei der Procedure habe ich weggelassen.

Ratte 8. Dez 2005 16:43

Re: Was macht Free genau in einem Array
 
Es könnte sein, dass die Zuweisung an die leere Speicherstelle nicht klappt. Lass mal das .free weg oder bau in die Schleife noch ein .free ein (jeweils für MyArray[i] bevor du das darrauffolgende zuweist.

Ratte

Flocke 8. Dez 2005 16:53

Re: Was macht Free genau in einem Array
 
Zitat:

Zitat von s.h.a.r.k
Nun ist es allerdings so, dass das Free schon eine Dekrementierung des Arrays bewirkt, was ich allerdings nicht wirklich verstehe...

Das wäre nur dann der Fall, wenn deine Klasse so `schlau´ wäre, sich im Destruktor selbst aus der Liste zu löschen.

// Nachtrag:

Zitat:

Zitat von Ratte
Lass mal das .free weg oder bau in die Schleife noch ein .free ein (jeweils für MyArray[i] bevor du das darrauffolgende zuweist.

Das ist Unsinn, weil er MyArray[i] ja vorher an MyArray[i - 1] zugewiesen hat.

QuickAndDirty 8. Dez 2005 17:00

Re: Was macht Free genau in einem Array
 
Könnte es sein das dieser fehler nur dann passiert wenn du das letzte verbleibende element aus dem array löschst?
Das DynArray wird nämlich über ein Interface auf @$00000000 gesetzt wenn alle element entfernt wurden.
sprich es wird freigegeben wenn der Referenzzähler 0 erreicht.

Ratte 8. Dez 2005 17:04

Re: Was macht Free genau in einem Array
 
@Flocke: stimmt natürlich. Sorry für's Unsinn verbreiten...

Ratte

s.h.a.r.k 9. Dez 2005 08:29

Re: Was macht Free genau in einem Array
 
Zitat:

Zitat von QuickAndDirty
Könnte es sein das dieser fehler nur dann passiert wenn du das letzte verbleibende element aus dem array löschst?
Das DynArray wird nämlich über ein Interface auf @$00000000 gesetzt wenn alle element entfernt wurden.
sprich es wird freigegeben wenn der Referenzzähler 0 erreicht.

Hm... nein, eben das nicht. Wenn ich das letzte lösche tritt da nie der Fehler auf, was ich allerdings etwas seltsam finde... daher verstehe ich auch nicht ganz warum es nicht klappen will. :gruebel:


PS: Kann es eventuell an meiner eigenen Destroy-Procedure liegen?! Die schaut nämlich wie folgt aus:
Delphi-Quellcode:
procedure TMyClass.Destroy;
begin
  // FSub -> Unterklasse von TMyClass
  FSub.Free;
  // FTest -> Variable von Typ TMyClass
  FTest.Free;
  inherited Destroy;
end;

Pseudemys Nelsoni 9. Dez 2005 08:59

Re: Was macht Free genau in einem Array
 
Zitat:

Zitat von s.h.a.r.k
Vielleicht habe ich mich etwas kompliziert ausgedrückt: Und zwar wie schon gesagt habe ich ein Array der Länge 10 und lösche nun z.B. das Fünfte über eine Procedure Delete, die wie folgt aussieht:

Delphi-Quellcode:
procedure TMyClass.Delete(Index: Integer);
var
  i : Integer;
begin
  MyArray[Index].Free;
  for i := Index to length(MyArray) - 2 do MyArray[i] := MyArray[i + 1];
  setlength(MyArray, length(MyArray) - 1);
end;
Nun ist es allerdings so, dass das Free schon eine Dekrementierung des Arrays bewirkt, was ich allerdings nicht wirklich verstehe...

PS: Sicherheitsabfragen bei der Procedure habe ich weggelassen.

Beim löschen eines Feldes Deines Arrays solltest du die schleife auf jeden Fall rückwärts(downto) laufen lassen.

xaromz 9. Dez 2005 09:01

Re: Was macht Free genau in einem Array
 
Hallo,
Zitat:

Zitat von Pseudemys Nelsoni
Beim löschen eines Feldes Deines Arrays solltest du die schleife auf jeden Fall rückwärts(downto) laufen lassen.

Bitte nicht. Die Schleife ist genau richtig. Um genau zu sein, der ganze Code, den ich hier bisher von s.h.a.r.k gesehen habe, ist richtig. Da Problem muss also woanders liegen.

Gruß
xaromz

RavenIV 9. Dez 2005 09:02

Re: Was macht Free genau in einem Array
 
mal dumm gefragt:
warum baust du das Array nicht in eine TList (oder Abkömmlinge davon) um?
dann hättest du bessere Verwaltungsfunktionen und müsstest dich nicht selber um alles kümmern.
oder brauchst du zwingend das Array?

s.h.a.r.k 9. Dez 2005 09:36

Re: Was macht Free genau in einem Array
 
Liste der Anhänge anzeigen (Anzahl: 1)
Das mit dem Array ar halt bisher mein Ansatz. Nur da sich das ganz nciht so verhält wie ich das gerne haben will, muss ich mir wohl was anderes einfallen lassen... Oder würde sich eventuell jemand bereit erklären mehrere Zeilen Code durchzuschauen und mir Tips geben?!?

So jetzt hab ich die Komponente im Anhang! Wäre echt klasse wenn einer von euch das mal durchschauen könnte!!! Vielen Dank schon mal im Vorraus!

mfg shark

Der_Unwissende 9. Dez 2005 10:48

Re: Was macht Free genau in einem Array
 
Hi,
hab mir mal deine Komponente angeschaut und ehrlich gesagt solltest du einiges an der ändern.
Aber ich komme erstmal auf deinen Fehler zu sprechen, wann genau tritt er denn auf? Was genau tust du denn? Ich hab jetzt einfach ein paar mal Add und Delete (sorry, gerade vergessen wie die Methode hieß) an verschiedenen Stellen aufgerufen und es kam kein Fehler.

Unsauber an deinem Programm ist es übrigens, dass du dein Array immer um eine Stelle vergrößerst oder verkleinerst (hatte hier auch schon jmd gesagt, oder?). Das ganze hat in Delphi echt Lustige Effekte, wenn ich mich nicht ganz irre gibt Delphi nämlich nicht den alten Platz frei, reserviert aber auch die neue Größe (alte Länge + 1) und damit hast du einen unnötigen Speicherbedarf.
Gute Alternativen (die intern auch auf Arrays zurückgreifen aber alles schön kapseln) sind die Listen TObjectList, TComponentList und TControlList, wobei erstere für dich wohl am ehesten in Frage kommt.

So, eine andere Sache ist dein FCount. Du kannst eigentlich gänzlich auf diese Variable verzichten (FCount = length(FItems) - 1), aber du solltest auch Klassenvariablen (imho) initialisieren. Die bekommen zwar von Delphi (soviel ich weiß) wirklich immer den Wert 0 zugewiesen, aber Eindeutigkeit schadet nicht.

Ganz schlimm ist es übrigens, dass du dein if then else immer schön in eine Zeile schreibst, solltest du wirklich ändern! Sorry, aber ist nicht meine persönliche Ansicht sondern einer der vielen Punkte zu gutem Code. Es lässt sich einfach leicht übersehen, dass ein else mit in der Zeile steht und es ist durch Einrückung leichter eine Hierachie sichtbar.
Zudem hast du beim Einzelschrittmodus natürlich überhaupt keinen Plan, ob er gerade das if oder das else abarbeitet.

Ja und zu guter Letzt noch was zu deinem Destruktor von TLaufzettel. Warum gibst du denn da nur den aktuellen TDigitalenLaufzettel frei? Deine Komponente TLaufzettel gibt es danach nicht mehr (wenn du ihr Free aufrufst). Also solltest du tunlichst alle TDigitalerLaufzettel freigeben. Und dazu solltest du dann über das ganze Array iterieren
Delphi-Quellcode:
for i := 0 to length(self.FItems) - 1 do
  begin
    self.FItems[i].Free;
  end;
finalize(self.FItems);
setLength(self.FItems, 0);

inherited destroy;
Ach ja, setVersion solltest du eher getVersion nennen :wink:

Ja, nicht böse sein, sind nur Stellen die du ändern solltest. Wie gesagt, dein Fehler ist bei mir nicht aufgetreten.

Gruß Der Unwissende

s.h.a.r.k 9. Dez 2005 13:13

Re: Was macht Free genau in einem Array
 
Also ganz zu Beginn muss ich zu meiner Verteidigung noch sagen, dass dies meine erste Komponente ist, die ich programmiere - habs halt bisher mit ein paar Tutorials zusammenklamüsert... :oops:

Nun gut, über eine "saubere" Syntax kann man sicherlich streiten, aber da hast du sicherlich nicht ganz unrecht. Habe halt bisher immer in diesem Stil programmiert und das von heute auf morgen mal so schnell aufzugeben ist nicht ganz so einfach. Habs Delphi nämlich in der Schule gelernt und der Lehrer hat es uns nun so beigebracht - lange Rede kurzer Sinn, nun zum eigentlich Inhalt:

Zitat:

Unsauber an deinem Programm ist es übrigens, dass du dein Array immer um eine Stelle vergrößerst oder verkleinerst (hatte hier auch schon jmd gesagt, oder?). Das ganze hat in Delphi echt Lustige Effekte, wenn ich mich nicht ganz irre gibt Delphi nämlich nicht den alten Platz frei, reserviert aber auch die neue Größe (alte Länge + 1) und damit hast du einen unnötigen Speicherbedarf.
Hier versteh ich nicht ganz was du damit meinst?! Wie genau sollte ich es denn sonst machen (wenn ich es dennoch mit einem Array probieren will?! :gruebel:

Zitat:

Gute Alternativen (die intern auch auf Arrays zurückgreifen aber alles schön kapseln) sind die Listen TObjectList, TComponentList und TControlList, wobei erstere für dich wohl am ehesten in Frage kommt.
Hab ich bisher zwar noch nix davon gehört oder jemals benutzt, aber wer Lesen kann ist ja bekanntlich im Vorteil - Werd mich zumindest mal drüber informieren! Oder fällt dir da spontan ein Tutorial ein? Brauchst dich aber nicht undbedingt bemühen, kann auch selber suchen :wink:

Zitat:

So, eine andere Sache ist dein FCount. Du kannst eigentlich gänzlich auf diese Variable verzichten (FCount = length(FItems) - 1), aber du solltest auch Klassenvariablen (imho) initialisieren. Die bekommen zwar von Delphi (soviel ich weiß) wirklich immer den Wert 0 zugewiesen, aber Eindeutigkeit schadet nicht.
Warum kann ich darauf verzichten?! Ich finds schöner so eine Variable zu haben! und was genau meinst du mit Klassenvariablen initialisieren?!? :wall:

Also das mit dem Free hab ich mal wieder verstanden *freu* Und das mit ver Version ist wenn ich etwas drüber nachdenke auch verständlich :wink:

Was ich grad noch gesehen hab - was machst diese Zeile hier, bzw zu was brauch ich die?
Zitat:

finalize(self.FItems);
PS: Was für ganz Dumme: Was heißt "iterieren" :oops:

Vielen Dank schon mal dass es recht nette Helferlein hier gibt :zwinker:

s.h.a.r.k 9. Dez 2005 14:08

Re: Was macht Free genau in einem Array
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hab nun im Anhang ein kleines Programm, mit dem ich meinte Komponente getestet habe...

Der_Unwissende 9. Dez 2005 14:13

Re: Was macht Free genau in einem Array
 
Zitat:

Zitat von s.h.a.r.k
Also ganz zu Beginn muss ich zu meiner Verteidigung noch sagen, dass dies meine erste Komponente ist, die ich programmiere - habs halt bisher mit ein paar Tutorials zusammenklamüsert... :oops:

Ist doch nicht schlimm, kein Grund zur Verteidigung. Sind alles nur Tipps (gelernt aus eigenen Fehlern, von denen es nicht wenige gab und immer noch genügend gibt).

Zitat:

Zitat von s.h.a.r.k
PS: Was für ganz Dumme: Was heißt "iterieren" :oops:

Hab mich wohl mal wieder unnötig kompliziert ausgedrückt :oops:

Eigentlich meinte ich gar nicht so spezielle Dinge. Iterieren heißt eigentlich nur, dass du etwas, nun ja, Durchläufst. Hätte auch sagen können, nimm jedes Element aus dem Array, jeder Durchlauf ist dabei eine Iteration.

Zitat:

Zitat von s.h.a.r.k
Nun gut, über eine "saubere" Syntax kann man sicherlich streiten, aber da hast du sicherlich nicht ganz unrecht. Habe halt bisher immer in diesem Stil programmiert und das von heute auf morgen mal so schnell aufzugeben ist nicht ganz so einfach. Habs Delphi nämlich in der Schule gelernt und der Lehrer hat es uns nun so beigebracht

Was den Stil angeht, so weiß ich dass es da (gerade von Lehrern verbreitet) sehr viele unterschiedliche Ansichten über guten Code-Stil gibt. Leider stimmt der (sogut wie?) nie mit den gegebenen Konventionen überein. Diese sind eigentlich sehr durchdacht und ich glaube ein Großteil der Menschen fängt anders an.
Ich weiß dass es nicht leicht ist sich umzugewöhnen (weswegen ich die Lehrer noch weniger verstehe), aber es ist wirklich ratsam. Hatte selbst mehr als lange alles andere als guten Stil und auch immer wieder kleine Fehler (dadurch). Diese zu finden ist bei besserem Stil wirklich schon deutlich leichter. Zudem musst du dich (wenn du dich an die Konvention hälst) nicht wirklich umgewöhnen, wenn du anderen Code liest. Gerade bei Projekten, die größer sind (und mit mehreren bearbeitet werden) ist es essenziell etwas einheitlicher zu arbeiten. Ich weiß wirklich gut, wie lange es bei mir gedauert hat, bis er mal durchgängig etwas besser wurde, aber du solltest es echt versuchen. Ist aber nur ein Tipp (über Pro und Contra lässt sich streiten und das haben andere schon genug gemacht).

Zitat:

Zitat von s.h.a.r.k
Zitat:

Unsauber an deinem Programm ist es übrigens, dass du dein Array immer um eine Stelle vergrößerst oder verkleinerst (hatte hier auch schon jmd gesagt, oder?). Das ganze hat in Delphi echt Lustige Effekte, wenn ich mich nicht ganz irre gibt Delphi nämlich nicht den alten Platz frei, reserviert aber auch die neue Größe (alte Länge + 1) und damit hast du einen unnötigen Speicherbedarf.
Hier versteh ich nicht ganz was du damit meinst?! Wie genau sollte ich es denn sonst machen (wenn ich es dennoch mit einem Array probieren will?! :gruebel:

Nun ja, wie gesagt ich kann mich auch irren, aber ich denke Delphi macht bei einem setLength(array, length(array) + 1) folgendes:
Es alloziert neuen Speicher der Größe length(array) + 1, kopiert nicht alle Werte von Array rüber sondern einen Verweis auf das Array und gibt das alte dann auch garnicht frei. Somit hast du nun statt length(Array) + 1 gleich 2 * length(Array) + 1 Speicher belegt.
Aber unabhängig davon, dauert es einfach eine gewisse Zeit, Speicher zu allozieren (allozieren = Speicher reservieren/zuweisen). Diese Zeit ist dabei für 1, 10, 10000 Bytes kaum unterschiedlich. Deshalb lohnt es sich nicht, nur 1 Zelle zu allozieren, dann eine weitere und wieder eine weitere...
Es ist viel perfomanter, gleich Speicher für 100 Zellen zu allozieren (setLength(array, length(array) + 100)) und sich dann zu merken, wieviele eigentlich benutzt werden (im Moment). Du führst dann sozusagen einen Index mit dir rum, der zeigt immer auf die letzte benutzte Zelle (nicht mehr automatisch die letzte des Arrays).
Das setzen dieses Index kostet weniger Zeit als Windows nach mehr Speicher zu fragen, die Daten aus dem Array zu kopieren und das alte Array freizugeben. Nähert sich dann dein Index dem ende des tatsächlichen Arrays, so wird die Größe wieder um 100 erhöht...

Wie gesagt, ich denke Listen machen dass in Delphi intern automatisch (mit guten Werten). An sich solltest du solche Kapselungen immer eher verwenden als Eigenlösungen. Damit will ich dir nicht hier automatisch dazu raten, um Erfahrungen zu sammeln oder aus Spaß oder was weiß ich (nimm's wörtlich), kannst du jederzeit natürlich auch eigene Lösungen verwenden.
Nur sind fertige Klassen halt gut getestet (von jedem Delphi-Benutzer der diese Klasse mal benutzt + Borland mind. vor Auslieferung). Dann fließt dort immer direkt gutes Wissen ein. Hier möchte ich auch keine Lösung schlecht reden, aber ich glaube die Jungs von MS, Borland, etc. machen sich schon ordentlich viele Gedanken, kennen die Windows-API recht gut und Theoretische Informatik (und damit Effiziente Datenstrukturen und Algorithmen) sind denen nicht gerade fremd.
Das alles schließt nicht aus, dass du einen besseren Weg findest, aber die Wahrscheinlichkeit, dass du einen Fehler machst (oder weniger Perfomant arbeitest) ist nicht gering.
Großes Problem bei deiner Lösung wäre halt auch, dass es ein Array ist, das du genau hier brauchst. Beim nächsten Projekt musst du wieder von vorne anfangen. Würdest du nun denken, hey, ein Fibonacci-Heap ist total toll (für's Speichern der TDigital..), dann müsstest du überall umstellen. Dann würdest du merken, hm, nettes Modell aber irgendwie doch nicht das Richtige und würdest z.B. zu einem Binominialheap (sorry, hab's heute wohl mit Heaps) umsteigen, dann wieder in jeder Klasse ändern.
Ändert Borland hingegen die Klasse TObjectList und du verwendest sie, so würde sich für dich nichts ändern (ist ja schließlich eine Kapselung/Black Box, was intern passiert ist dir egal).

Zitat:

Zitat von s.h.a.r.k
Warum kann ich darauf verzichten?! Ich finds schöner so eine Variable zu haben! und was genau meinst du mit Klassenvariablen initialisieren?!? :wall:

Gut, hatte mal einen Kollegen (der nebenbei bemerkt mittlerweile Informatik-Lehrer ist), der hat immer gerne einen Haufen Variablen eingeführt, die man eigentlich nicht gebraucht hätte. Die meisten davon waren dann recht global und wurden irgendwo mal benutzt und an anderen Stellen auch mal nicht. Wenn du nun an solch einem Code arbeitest, passiert es schnell, dass du auf das letzte Element mit Array[length(Array) - 1] zugreifst und dort irgendwas machst (es z.B. löscht), dann hast du das Problem, dass ein solcher Zeiger nicht mehr aktuell ist. Aber ist nur mein persönliches Trauma, wenn du sie schöner findest ist das ok (bin doch auch nur unwissend!)

Mit Klassenvariable initialisieren meine ich, dass FCount im Konstruktor ruhig den Wert 0 zugewiesen bekommen sollte. Man kann jetzt hier sagen, dass das Delphi automatisch macht, aber es ist sehr Borland Delphi spezifisch und damit fehlt dir eigentlich eine Garantie dafür. Bei Referenzen auf Klassen, weiß man, dass die nil sind (Achtung, rede immer noch über Klassenvariablen), bei prim. Typen sollte man ruhig initialisieren.
Bei lokalen Variablen muss man sogar initialisieren (wollte ich nur der Vollständigkeit halber sagen), sonst kommt es schnell zu richtig bösen Überraschungen.

Zitat:

Zitat von s.h.a.r.k
Was ich grad noch gesehen hab - was machst diese Zeile hier, bzw zu was brauch ich die?
Zitat:

finalize(self.FItems);

Die gibt den reservierten Heap des Array frei.

So, noch mal schnell etwas zu TObjectList, eine ziemlich einfache Komponente. Listen kommen immer dann zum Einsatz, wenn du eine Variable Länge von Elementen hast (gibt auch dann noch bessere Datenstrukturen). Aber eine Liste macht nicht mehr, als sich ein Element zu merken und dazu den Nachfolger. Für den Nachfolger kannst du dann wieder gucken ob er einen Nachfolger hat. Ist das gespeicherte Element nil, ist die Liste leer. Findest du ein Element das keinen Nachfolger hat, ist es das letzte Element (gilt für einfach verkettete Listen). Du kannst dir ganz Analog auch den Vorgänger merken.
Anders als Arrays musst du die Größe nicht kennen, du kannst einfach beim einfügen dem letzten Element seinen Nachfolger anhängen. Wenn du eine Element E1, dass in der Liste zwischen E0 und E2 steht entfernst, so setzt du E2 als Nachfolger von E0 ein, fertig.
Anders als bei Arrays, kannst du aber nicht wahlfrei auf einen Index zugreifen. Du weißt halt nicht, wieviele Elemente deine Liste hat.
Nun ja, ich weiß nicht ob du Listen nicht schon gut kennst (es gibt natürlich auch doppelt verkettete, Vorgänger und Nachfolger bekannt, zyklische Letzter zeigt auf ersten, ...). Wäre aber ein anderes Thema.

In Delphi gibt es auch verschiedene Arten von Listen. Du hast eine TList, die speichert nur Pointer, wirst du eher nicht brauchen. Dann gibt es noch die wichtigeren TObjectList, TComponentList und TControlList (wahrscheinlich auch weitere, andere).
Aus ihren Namen geht schon hervor was sie speichern, TObjectList alles vom Typ TObject (und Nachfahren), TComponentList alles vom Typ TComponent (und Nachfahren), TControlList (na rate mal).

Damit kannst du also ein TControl (und Nachfahren) in jeder dieser Listen speichern, ein TObject aber nur in TObjectList. Einträge fügt man einfach mit einem Add hinzu, mit Delete wird ein Objekt entfernt. Extract entfernt ein Objekt und liefert es dir auch zurück.
Wenn du nun ein Objekt mit Extract entfernst, bekommst du entsprechend der Liste ein TObject, ein TComponent oder ein TControl. Dabei ist es egal was für einen Typen du mal reingesteckt hast, du hast nach dem rausholen (oder auch in der Liste) nur die Eigenschaften des entsprechenden Vorfahren. Du musst also einfach nur casten und hast wieder alle Eigenschaften.

Wichtig ist, diese drei Listen haben auch die Eigenschaft Items, die ist ein Array auf dass du wie gewohnt zugreifen kannst. Hinzufügen oder entfernen solltest du aber unbedingt über die Methoden Add/Delete/Extract, damit die Größe des Arrays auch korrekt angepasst wird.
Hier noch ein kleines Beispiel:

Delphi-Quellcode:
type
  TMyClass = class(TControl)
  ....
  ....
  end;

...

var list : TObjectList;
    i : Integer;
begin
  list := TObjectList.Create;
 
  // 3 neue Instanzen hinzufügen
  list.Add(TMyClass.Create);
  list.Add(TMyClass.Create);
  list.Add(TMyClass.Create);

  // letztes Element löschen
  list.delete(2);

  // alle Elemente einen Namen (name + Nummer) geben
  if list.Count > 0 then
    begin
      for i := 0 to list.Count - 1
        begin
          // TObject hat keinen Namen -> casten
          TMyClass(list.Items[i]).name = 'name' + IntToStr(i);
        end;
    end;

  // Liste freigeben, vorher alle Elmente löschen
  if list.Count > 0 then
    begin
      for i := 0 to list.Count - 1
        begin
          // Free ist Methode von TObject, kein casten nötig
          list.Items[i].Free;
        end;
  list.Free;
end;
Gruß Der Unwissende

Der_Unwissende 9. Dez 2005 19:33

Re: Was macht Free genau in einem Array
 
Hi,
so musstest zwar etwas warten, aber ich habs mir jetzt noch mal anschauen können und den Fehler nun auch gefunden.
Ist eigentlich nichts weiter wildes (man kann es leicht übersehen).

Delphi-Quellcode:
  if (Index > -1) and (Index < FCount) then begin
    for i := Index to FCount - 2 do FItems[i] := FItems[i + 1];
    FItems[FCount - 1].Free;
    setlength(FItems, FCount - 1);
    FCount := length(FItems);

    if (Index = FCount) then dec(Index);
      FItemIndex := Index;
      if (Index > -1) then FActiveIndex := FItems[Index] else FActiveIndex := nil;
  end;
Guck dir mal kurz Schritt für Schritt an was du machst. Du hast ein Element an der Stelle Index, das du gerne Löschen würdest. Nun kontrollierst du ob Index gültig ist und ok, wenn es der Fall ist, werden alle Elemente die dahinter (aufpassen, nur die Referenz) nach vorne kopiert.
Danach gibst du das Element an der Stelle FCount - 1 frei. Hier liegt dein Fehler. Du kannst natürlich nicht mehr das Element an der Stelle Index freigeben, da liegt schließlich eine andere Referenz, aber das letzte Element ist auch nicht das, das du freigeben willst.
Nun ja, sagen wir deine Liste ist 5 lang und sagen wir, du hättest ein FItems wie Folgt : [1,2,3,4,5] und du löscht 2, dann bekommst du was? [1,3,4,5,5] (alles um eine Stelle nach links kopiert), dann gibst du das letzte Element frei [1,3,4,nil,nil] (weil gleiche Referenz). Wenn du nun ein Free auf nil anwenden willst knallt's.

Deshalb brauchst du einen Puffer, indem du dir das Element an der Stelle Index merkst

Delphi-Quellcode:
procedure TLaufzettel.Delete(Index: Integer);
var i : Integer;
    buffer : TDigitalerLaufzettel;
begin
  if (Index > -1) and (Index < FCount) then begin
    buffer := FItems[Index];
    for i := Index to FCount - 2 do
      FItems[i] := FItems[i + 1];
    buffer.Free;
    setlength(FItems, FCount - 1);
    FCount := length(FItems);

    if (Index = FCount) then dec(Index);
      FItemIndex := Index;
      if (Index > -1) then FActiveIndex := FItems[Index] else FActiveIndex := nil;
  end;
end;
Dann sollte alles klappen. (hoff ich jetzt mal, ungetesteter Weise)

Gruß

QuickAndDirty 10. Dez 2005 09:39

Re: Was macht Free genau in einem Array
 
Nutze
FreeAndNil(obj:Tobject);
!!!
wenn du instanzen frei gibst.

und so solltest beim Löschen von elementen wirklich rückwärts iterieren

s.h.a.r.k 12. Dez 2005 10:49

Re: Was macht Free genau in einem Array
 
Guten Morgen :wink:

Hab jetzt die Komponente umgeschrieben - mit TObjectList - und hat bisher wunderbar geklappt! Hab sogar dann mein eigenes FCount wegfallen lassen können, da ich das ja dann mit dem TObjectList mitgeliefert bekommen habe.

[Edit]

Also ich mag ja wirklich nicht nerven mit meinem Problem, aber zurzeit klappt es ja auch mit dem TObjectList, allerdings nur wenn ich die Free-Procedure dieser Komponente nicht anfasse! Denn wenn ich das mache bekommen ich lauter AccesViolations... Wenn ich nun meine Anwendung, d.h. wohl eher die RAM-Auslastung, per Task-Manager anschaue, dann sehe nach dem Schließen, dass alles wieder freigegeben wird. Aber warum?!?

[/Edit]

Der_Unwissende 12. Dez 2005 18:06

Re: Was macht Free genau in einem Array
 
Wie bereits gesagt, beim letzten Mal hast du einfach eine Stelle (die letzte) des Arrays gelöscht. Dort steht nur eine Referenz auf ein Objekt, nicht das Objekt selbst. Solange du nun mehr als eine Referenz auf ein und das Selbe Objekt hast, kann dieses Problem auftauchen. Gibst du mittels Referenz.Free das Objekt frei, so sind alle anderen Referenzen Zeiger auf einen ungültigen Speicherbereich.
Bei deinem Array müsste es sich (immer noch ungetesteter Weise) beheben lassen, wenn du eine Referenz auf das zu löschende Objekt behälst, dann alle Einträge verschiebst und danach die Liste um ein Element verkleinerst sowie diese (lokale) Referenz auf das zu löschende Objekt freigibst.
Bei einer Liste sollte dies ja entfallen, die einzigste Möglichkeit die mir nun einfällt, wäre es dass du leere Referenzen (nil) speicherst, auf eine leere Liste zugreifst oder mehr als eine Referenz auf ein Objekt speicherst.

Gruß Der Unwissende


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