![]() |
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:
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???
MyArray[9].Free;
setlength(MyArray, length(MyArray) - 1); mfg shark |
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:
Es kommt keine Exception.
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; Könnte es vllt sein dass du nochmal auf Element [9] zugreifst? |
Re: Was macht Free genau in einem Array
Zitat:
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 |
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:
Nun ist es allerdings so, dass das Free schon eine Dekrementierung des Arrays bewirkt, was ich allerdings nicht wirklich verstehe...
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; PS: Sicherheitsabfragen bei der Procedure habe ich weggelassen. |
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 |
Re: Was macht Free genau in einem Array
Zitat:
// Nachtrag: Zitat:
|
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. |
Re: Was macht Free genau in einem Array
@Flocke: stimmt natürlich. Sorry für's Unsinn verbreiten...
Ratte |
Re: Was macht Free genau in einem Array
Zitat:
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; |
Re: Was macht Free genau in einem Array
Zitat:
|
Re: Was macht Free genau in einem Array
Hallo,
Zitat:
Gruß xaromz |
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? |
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 |
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:
Ach ja, setVersion solltest du eher getVersion nennen :wink:
for i := 0 to length(self.FItems) - 1 do
begin self.FItems[i].Free; end; finalize(self.FItems); setLength(self.FItems, 0); inherited destroy; Ja, nicht böse sein, sind nur Stellen die du ändern solltest. Wie gesagt, dein Fehler ist bei mir nicht aufgetreten. Gruß Der Unwissende |
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:
Zitat:
Zitat:
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:
Vielen Dank schon mal dass es recht nette Helferlein hier gibt :zwinker: |
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...
|
Re: Was macht Free genau in einem Array
Zitat:
Zitat:
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:
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:
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:
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:
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:
Gruß Der Unwissende
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; |
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:
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.
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; 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:
Dann sollte alles klappen. (hoff ich jetzt mal, ungetesteter Weise)
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; Gruß |
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 |
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] |
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