Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Element aus einer TList (Generics.Collection) löschen


Hirschi - Fr 03.12.10 18:54
Titel: Element aus einer TList (Generics.Collection) löschen
Hallo Leute,
seit ein paar Stunden hänge ich an folgendem Problem:

Ich brauche einen eigenen Datentyp. Dafür wollte ich einen Record nehmen. Ungefähr so:

Delphi-Quelltext
1:
2:
3:
4:
type  TStep = record
     fromPos  : Integer;
     toPos    : Integer;
end;

Davon will ich beliebig viele in einer Liste verwalten. Dazu verwende ich eine Liste:

Delphi-Quelltext
1:
Steps : TList<TStep>;                    


Die wird nun erstellt und ein paar Elemente hinzugefügt:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
Steps := TList<TStep>.Create();
...
st.fromPos := Random(100);
st.toPos   := Random(100);
Steps.Add(st);
st.fromPos := Random(100);
st.toPos   := Random(100);
Steps.Add(st);


Das klappt auch alles ohne Fehler. Will ich aber nun ein beliebiges Element löschen, bekomme ich eine Zugriffsverletzung:

Delphi-Quelltext
1:
Steps.Delete(Steps.Count-1);                    

Wenn ich statt meinem Record einen primitven Datentyp (z.B. Integer) nehme, dann funktionierts es. Ich muss also irgendwas mit dem Record nicht verstanden haben. Hoffe, ihr habt einen Tipp.

MfG
Hirschi


Narses - Fr 03.12.10 19:04

Moin und :welcome: im Forum!

Du hast leider einen wichtigen Teil in deinem Code nicht gezeigt: die Speicherverwaltung. ;) Weiterhin wäre es auch nicht ganz unwichtig, wie die Fehlermeldung genau aussieht.

btw: Nimm lieber ein Objekt und die TObjectList, das ist besser als das Pointergehampel mit der TList... :? :nixweiss:

cu
Narses


bummi - Fr 03.12.10 21:00

@Narses

ich vermute alle Pointer zeigen auf die gleiche Variable

Delphi-Quelltext
1:
 st:TStep                    

möglicherweise, sogar auf dem Stack...


Hirschi - Fr 03.12.10 21:02

Danke für die schnelle Antwort, Narses! :)
user profile iconNarses hat folgendes geschrieben Zum zitierten Posting springen:
Du hast leider einen wichtigen Teil in deinem Code nicht gezeigt: die Speicherverwaltung. ;)
Na, da gibt es keine besondere. Deshalb nehm ich doch TList aus der Uni Generics.Collection und nicht eine normale TList.
user profile iconNarses hat folgendes geschrieben Zum zitierten Posting springen:

Weiterhin wäre es auch nicht ganz unwichtig, wie die Fehlermeldung genau aussieht.
Es ist schlicht eine Zugriffsverletzung auf eine Adresse im Speicher. Sie wird geworfen, wenn ich das Element löschen will. Insofern kann ich das nicht genauer schreiben, denn die Adresse ist ja jedesmal anders.
user profile iconNarses hat folgendes geschrieben Zum zitierten Posting springen:
btw: Nimm lieber ein Objekt und die TObjectList, das ist besser als das Pointergehampel mit der TList... :? :nixweiss:
Richtig, das "Pointergehampel" wollte ich umgehen. ;)

MfG
Hirschi


jaenicke - Fr 03.12.10 21:15

user profile iconHirschi hat folgendes geschrieben Zum zitierten Posting springen:
Deshalb nehm ich doch TList aus der Uni Generics.Collection und nicht eine normale TList.
Das hat aber nichts damit zu tun, dass du auch neuen Speicher für jeden Record allozieren und evtl. am Ende wieder freigeben musst (ob die Freigabe bei der Collection automatisch passieren kann, weiß ich nicht). :nixweiss:

user profile iconHirschi hat folgendes geschrieben Zum zitierten Posting springen:
Richtig, das "Pointergehampel" wollte ich umgehen. ;)
Und warum machst du es dir dann unnötig schwer? Genau dafür gibt es wie schon geschrieben wurde Klassen und Objekte.


Hirschi - Mo 06.12.10 16:48

So wie ich es im Eingangsbeitrag beschrieben habe, funktioniert es auch bei einem Kollegen auf dem Rechner. Er verwendet allerdings Delphi 2010. Heißt, es muss wohl an meiner D2009-Installation liegen...sehr ärgerlich. Ich habe das nun mit Debug-DCUs durchlaufen und das Programm stützt in der DoDelete-Prozedur der Generics.Collection.pas bei der Zeile
FItems[Index] := Default(T);
ab. An der Prozedur ist von D2009 auf D2010 nichts passiert...tja, dann werd ich wohl was anderes (TObjectList) nehmen... Aber, theoretisch ginge mein Weg auch ;)

MfG
Hirschi


Narses - Mo 06.12.10 17:07

Moin!

user profile iconHirschi hat folgendes geschrieben Zum zitierten Posting springen:
So wie ich es im Eingangsbeitrag beschrieben habe, funktioniert es auch bei einem Kollegen auf dem Rechner.
[...]
Aber, theoretisch ginge mein Weg auch
Wenn du mit records arbeitest, dann wird das ganz sicher nicht korrekt funktionieren, denn für die Speicherverwaltung bei records ist immer zusätzlicher Code nötig, das hat Delphi (und auch TP) noch nie selbst gemacht. :mahn: Insofern wundert mich, dass es irgendwo "laufen soll" (was man dann noch mal näher untersuchen sollte, heißt ja vielleicht auch nur: "stürzt nicht sofort ab" :mrgreen:)... :nixweiss:

cu
Narses


Hirschi - Mo 06.12.10 18:06

user profile iconNarses hat folgendes geschrieben Zum zitierten Posting springen:
Moin!

Wenn du mit records arbeitest, dann wird das ganz sicher nicht korrekt funktionieren, denn für die Speicherverwaltung bei records ist immer zusätzlicher Code nötig, das hat Delphi (und auch TP) noch nie selbst gemacht. :mahn: Insofern wundert mich, dass es irgendwo "laufen soll" (was man dann noch mal näher untersuchen sollte, heißt ja vielleicht auch nur: "stürzt nicht sofort ab" :mrgreen:)... :nixweiss:

Speicher beziehen und freigeben macht die TList<T> für dich, denn die Generics-Sachen haben eine eigene Speicherverwaltung.

Übrigens läuft es jetzt auch bei mir (endlich!), da ich eine Update der IDE gemacht habe. Auch bei einem anderen Kollgen mit D2009 läuft es genau wie beschrieben.

MfG
Hirschi


Narses - Mo 06.12.10 18:39

Moin!

user profile iconHirschi hat folgendes geschrieben Zum zitierten Posting springen:
Speicher beziehen und freigeben macht die TList<T> für dich, denn die Generics-Sachen haben eine eigene Speicherverwaltung.
Das glaube ich gerne, wenn es um Objekte geht. ;) Aber bei records nicht. :idea: Ab D2k9 war doch FastMM mit dabei, da kann man doch eine Memory-Leak-Prüfung einschalten, das würde ich an deiner Stelle mal tun und sehen, ob da wirklich das passiert, was du erwartest... :?

cu
Narses


Kha - Mo 06.12.10 20:19

Ich kann zwar nicht selbst nachschauen, aber ich fürchte, ihr seht gerade Zeiger, wo keine sind ;) . Nach der Instanzierung der Generics dürfte ja ganz grob das (zumindest innerhalb des Compilers) dranstehen:

Delphi-Quelltext
1:
2:
3:
4:
5:
type TList<TStep> = class
  ...
  var FItems : array of TStep;
  ...
  public procedure Add(aObj: TStep);

Man kann natürlich auch eine TList<TStep^> instanzieren (geh ich einfach mal von aus :) ), aber im obigen Fall sollten keine Zeiger beteiligt sein.


bummi - Mo 06.12.10 20:49

Ich habe es nachdem ich es auch nicht glauben wollte bei folgendem Thread

http://www.delphipraxis.net/156480-problem-mit-dynamischem-array-record-new-post.html

einmal komplett durchgespielt, geht tatsächlich problemlos und sauber, zumindest unter XE.


Narses - Mo 06.12.10 23:26

Moin!

OK, überzeugt. ;) (das mit dem internen dynamischen Array ist natürlich die Erklärung! :think:)

cu
Narses


Hirschi - Do 09.12.10 11:16

Danke für das arbeitsreiche Schlusswort, bummi ;) Und danke an alle Mitdiskutanten!

MfG
Hirschi