Files |  Tutorials |  Articles |  Links |  Home |  Team |  Forum |  Wiki |  Impressum

Aktuelle Zeit: Fr Jul 04, 2025 22:41

Foren-Übersicht » Programmierung » Allgemein
Unbeantwortete Themen | Aktive Themen



Ein neues Thema erstellen Auf das Thema antworten  [ 14 Beiträge ] 
Autor Nachricht
 Betreff des Beitrags: Verkettete Listen
BeitragVerfasst: Fr Nov 09, 2007 12:04 
Offline
DGL Member
Benutzeravatar

Registriert: Sa Jul 22, 2006 00:43
Beiträge: 30
Wohnort: Borland
Hi :)

Ich habe vor diverse Elemente per verketteten Listen anzulegen um so flexibel zu sein und alle Elemente eines Typs auf einmal ansprechen zu können, egal wieviele es gerade sind.

Leider habe ich nicht ganz so viel Erfahrung damit und weiß gerade nicht warum ich einen Fehler bekomme.

Habe den Code mal auf das nötigste für mein Problem reduziert:

In Zeile 80 bekomme ich dann eine Zugriffsverletzung wenn ich die Textur dem neuen Objekt zuweisen möchte, allerdings weist er die Werte der paar letzten Zeilen (76-79) normal zu.

Code:
  1.  
  2. //Schiffsklasse
  3. type
  4.   PShip = ^TShip;
  5.   TShip = class
  6.   private
  7.   //
  8.   public
  9.     Rotate, breite, hoehe: single;
  10.     PosVect  : TVector3f;
  11.     texture: glUInt;
  12.  
  13.     pNaechster : PShip;
  14.     pVorheriger : PShip;
  15.  
  16.     constructor create;
  17.     procedure move(speed: single);
  18.     procedure draw;
  19.   end;
  20.  
  21.   type
  22.     TForm1 = class(TForm)
  23.       Button1: TButton;
  24.       Button2: TButton;
  25.       Button3: TButton;
  26.       Button4: TButton;
  27.       Button5: TButton;
  28.       Button6: TButton;
  29.       Button7: TButton;
  30.       TrackBar1: TTrackBar;
  31.       DXInput1: TDXInput;
  32.       Button8: TButton;
  33.       procedure FormCreate(Sender: TObject);
  34.       procedure FormResize(Sender: TObject);
  35.       procedure FormDestroy(Sender: TObject);
  36.       procedure IdleHandler(Sender: TObject; var Done: Boolean);
  37.       procedure Button8Click(Sender: TObject);
  38.     private
  39.       StartTime, TimeCount, FrameCount  : Cardinal; //FrameCounter
  40.       Frames, DrawTime                  : Cardinal; //& Timebased Movement
  41.       procedure SetupGL;
  42.       procedure Init;
  43.       procedure Render;
  44.       procedure ErrorHandler;
  45.     public
  46.       DC                                : HDC;  //Handle auf Zeichenfläche
  47.       RC                                : HGLRC;//Rendering Context
  48.       procedure DrawShips;
  49.     end;
  50.  
  51.   var
  52.     Form1: TForm1;
  53.  
  54.     //Für verkettete Liste
  55.     pStart     :  PShip = Nil;  //erstes Element in der Liste
  56.     pAktuell   :  PShip = Nil;  //aktuelles Element in der Liste
  57.     pEnde        :  PShip = Nil;  //letztes...
  58.     pTemp      :  PShip = Nil;  //Temp für Löschung
  59.  
  60.     venator, venator2: TShip;
  61.  
  62.   const
  63.     NearClipping = 1;
  64.     FarClipping  = 1000;
  65.  
  66.   implementation
  67.  
  68.   {$R *.dfm}
  69.  
  70. procedure TForm1.Button8Click(Sender: TObject);
  71. begin
  72.   //Testweise ein neues Schiff in die verkettete liste einfügen
  73.   //Element anlegen
  74.   new(pAktuell);
  75.  
  76.   pAktuell.Rotate:= 360;
  77.   pAktuell.PosVect[0] := 300;
  78.   pAktuell.PosVect[1] := 300;
  79.   pAktuell.PosVect[2] := 1;
  80.   LoadTexture('testvenator.tga', pAktuell.texture, false);
  81.   pAktuell.breite:=128;
  82.   pAktuell.hoehe:=256;
  83.  
  84.   //erstes Element anlegen
  85.   if((pStart=nil) AND (pEnde=nil)) then begin
  86.     pStart:=pAktuell;
  87.  
  88.     pAktuell.pNaechster:=nil;
  89.     pAktuell.pVorheriger:=nil;
  90.   end else begin
  91.     //Element an die Kette anhängen
  92.     pEnde.pNaechster:=pAktuell;
  93.     pAktuell.pVorheriger:=pEnde;
  94.   end;    
  95.  
  96.   //Aktuellen Datensatz hinten anhängen
  97.   pEnde:=pAktuell;
  98. end;
  99.  
  100. //Prozedur um alle Schiffe auf dem Spielfeld zu zeichnen
  101. //Durchgehen aller Elemente in der verketteten Liste
  102. procedure TForm1.DrawShips;
  103. begin
  104.   //Ist ein Objekt vorhanden?
  105.   if(pStart<>nil) then begin
  106.     if(pStart=pEnde)then begin
  107.       //Nur ein Element
  108.       pStart.draw;
  109.     end else begin
  110.       //Beim ersten Element anfangen
  111.       pAktuell:=pStart;
  112.       repeat
  113.         pAktuell.draw;
  114.         pAktuell:=pAktuell.pNaechster;
  115.       until(pAktuell<>pEnde);
  116.     end;
  117.   end;
  118. end;
  119.  


Weiß jemand von euch Rat? Was mache ich falsch? Wenns ein Problem mit dem Pointer ist müsste es ja auch in Zeile 76-79 eine Zugriffsverletzung geben oder übersehe ich da was?

Vielen Dank :)


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Fr Nov 09, 2007 13:03 
Offline
DGL Member
Benutzeravatar

Registriert: Do Dez 05, 2002 10:35
Beiträge: 4234
Wohnort: Dortmund
Das Wichtigste vorweg. Klassen sind bereits Pointer. Da verkettete Liste meistens aber mit Record gemacht werden braucht man dort einen zusätzlichen Pointertypen. Du brauchst so aber keinen.

Und da du Klassen als Einträge benutzt kannst du auch nicht New benutzen. Klassen immer über den Konstruktor erstellen..

Also würde der Code in etwa so aussehen.

Code:
  1. var
  2.   pStart : TShip = Nil; //erstes Element in der Liste
  3.   pAktuell : TShip = Nil; //aktuelles Element in der Liste
  4.  
  5.  
  6. procedure TForm1.Button8Click(Sender: TObject);
  7. begin
  8.   //Testweise ein neues Schiff in die verkettete liste einfügen
  9.   //Element anlegen
  10.   pAktuell := TShip.Create;
  11.  
  12.   pAktuell.Rotate:= 360;
  13.   pAktuell.PosVect[0] := 300;
  14.  
  15.   //erstes Element anlegen
  16.   if((pStart=nil) AND (pEnde=nil)) then begin
  17.     pStart:=pAktuell;

Ich denke wenn du deinen Code komplett darauf umgetstellt hast sollte es funktionieren. Denke ich. Ob die Pointer richtig gesetzt werden darauf habe ich jetzt nicht geachtet.

2 Hinweise noch am Rande. Verkettete Listen lohnen sich nur wenn du Einträge schnell umhängen musst, den Ersten löschen musst etc. Wenn du sagen wir mal 200 Schiffe hast von denen evtl mal eines gelöscht wird etc, dann kannst du auch locker eine Liste benutzen. Denn wenn du auf Element 50 zugreifen möchtest musst du so immer erst mal die Liste durchsuchen und das dauert mitunter länger als das simple Speicher umkopieren bei einer TList. Unter anderem erleichtert das den Code auch nicht immer.

Bei Pointern solltest du grundsätzlich IMMER dereferenzieren. Delphi macht es zwar weitestgehend automatisch aber ich habe es schon mal erlebt, dass er es in gewissen Situationen nicht getan hat. Deswegen immer PointerVariable^.blah := .... Und alleine auch schon weil man den Code so einfacher verstehen kann. Auch du selber wenn du in 2 Monaten den mal wieder siehst.


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Fr Nov 09, 2007 13:18 
Offline
DGL Member
Benutzeravatar

Registriert: Sa Jul 22, 2006 00:43
Beiträge: 30
Wohnort: Borland
Hi, vielen Dank für deine Antwort :)

Wieder was dazu gelernt. Wichtig war mir eben, dass ich für OpenGL alle Objekte der Klasse auf einmal ansprechen/zeichnen kann und, dass das Einfügen und Entfernen nicht an irgendwelche Arrays (auch nicht dynamische) gebunden ist, da die Anzahl der Objekte offen sein soll. Wie genau würde das bei einer TList aussehen? Ich habe die noch nicht benutzt.

Frage nebenbei: Wie oben geschrieben habe ich auch nach einer Möglichkeit gesucht eine Methode der Klasse für alle existierenden Objekte der Klasse ausführen zu lassen, wie könnte ich sowas machen ohne Arrays oder Listen? Gibt es da auch eine Möglichkeit?

Gruß
MB


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Fr Nov 09, 2007 13:51 
Offline
DGL Member
Benutzeravatar

Registriert: Do Dez 05, 2002 10:35
Beiträge: 4234
Wohnort: Dortmund
Zitat:
und, dass das Einfügen und Entfernen nicht an irgendwelche Arrays (auch nicht dynamische) gebunden ist, da die Anzahl der Objekte offen sein soll.

Folgendes musst du mir mal bitte erklären. Denn für mich sieht es so aus als ob du ziemlich genau weißt warum du es so nicht machen möchtest? Bzw würde mich mal interessieren was du unter Offen verstehst. Also welche Maximalanzahl dir da durch den Kopf geht.

Eine TList ist eine Klasse die ein dynamisches Array kappselt. Also intern benutzt es auch nur ein dynamisches Array. Und du erstellst davon ein Instanz. Kannst bequem Einträge hinzufügen, löschen etc und dir zurück geben lassen.
Code:
  1. var
  2.   List: TList;
  3. begin
  4.   List := TList.Create;
  5.  
  6.   List.Add(Ship);
  7.   List.Add(Ship);
  8.   List.Add(Ship);
  9.   List.Add(Ship);
  10.  
  11.   for Idx := 0 to List.Count -1 do
  12.     TShip(List[Idx]).Draw;
  13.  
  14.   List.Free;
  15. end;
Das mal als kleines Beispiel. Wenn du sehr viele Einträge hast und die Einträge einzeln löschen möchtest bietet es sich an die Liste von Hinten zu löschen. Da ansonsten die Einträge jedes mal verschoben werden müssen. Das ist außer an zeitkritischen Stellen oder bei gigantischen Eintragszahlen aber auch nicht so tragisch und dadurch, dass der Speicher direkt kopiert wird ist es auch nicht allzu langsam.

Zitat:
Wie oben geschrieben habe ich auch nach einer Möglichkeit gesucht eine Methode der Klasse für alle existierenden Objekte der Klasse ausführen zu lassen, wie könnte ich sowas machen ohne Arrays oder Listen? Gibt es da auch eine Möglichkeit?

Ich muss gestehen, dass ich nicht ganz weiß was du möchtest. Ist das so etwas wie ich habe 200 Instanzen von TShip erstellt und möchte nun von allen 200 eine Methode aufrufen? Wenn ja dann geht das nur in du alle 200 irgendwo registrierst. Zum Beispiel in einer Liste (egal welche Form). Und anschließend musst du durch die Liste und die Methode aufrufen.


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Fr Nov 09, 2007 14:38 
Offline
DGL Member
Benutzeravatar

Registriert: Sa Jul 22, 2006 00:43
Beiträge: 30
Wohnort: Borland
Im Grunde genommen möchte ich zur Zeit zum testen garkein Limit, sprich so viel wie der Speicher verkraftet :D Um mal zu testen wie hoch man das je nach Rechner treiben kann. Aber wenn ich irgendeine Zahl sagen soll wirds schwierig, da ich noch nicht genau abschätzen kann wieviele es später werden. Könnte mir vorstellen, dass es sicher mehrere hundert werden oder sogar mehr.

Ich dachte es wäre wirklich schneller ohne Arrays aber das mit der TList war mir so noch nicht bekannt. Ich denke ich werde das mal testen.

Zitat:
Ich muss gestehen, dass ich nicht ganz weiß was du möchtest. Ist das so etwas wie ich habe 200 Instanzen von TShip erstellt und möchte nun von allen 200 eine Methode aufrufen? Wenn ja dann geht das nur in du alle 200 irgendwo registrierst. Zum Beispiel in einer Liste (egal welche Form). Und anschließend musst du durch die Liste und die Methode aufrufen.


Exakt, schließlich muss ja jedes Schiff gezeichnet werden und diese Routine habe ich in eine Methode der Schiffsklasse gepackt.

Vielen Dank :)


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Fr Nov 09, 2007 16:04 
Offline
DGL Member
Benutzeravatar

Registriert: Do Dez 05, 2002 10:35
Beiträge: 4234
Wohnort: Dortmund
Zitat:
Exakt, schließlich muss ja jedes Schiff gezeichnet werden und diese Routine habe ich in eine Methode der Schiffsklasse gepackt.

Da wirst du um eine Iterative Lösung nicht drumherum kommen.

Zitat:
dass es sicher mehrere hundert werden oder sogar mehr.

Also ich denke es würde erst ab einer 5 stelligen Anzahl wirklich spannend werden. Wobei es sehr stark darauf ankommt was du tust. Wenn du sehr häufig vom Anfang Einträge löschen musst könnte eine verkettete Liste wesentlich besser sein. Da bei einer TList muss wie gesagt immer der Speicher kopiert werden, da die Daten immer an einem Stück liegen. Allerdings beim Häufigen erstellen und Freigeben dürften Klassen auch nicht so gut sein. Da dann der Speichermanager von Delphi sehr viel zu tun bekommt.

Allerdings sage ich jetzt einfach mal so, dass bei allem unter 1000 es so gut wie kaum einen markanten Unterschied geben wird. Selbst wenn die Daten häufiger mal kopiert werden müssen.

Bei großen Datenmengen kommt es immer sehr stark darauf an was du tust und damit machen willst. Teilweise fähr man besser wenn man mehr Speicher verbraucht. Teilweise ist die eine Lösung zwar schnell wenn man Einträge einfügen will allerdings beim Durchsuchen wieder langsam. Oder beim Einfügen langsam und dafür beim Durchsuchen schnell. Das hängt halt grundsätzlich immer daran was du tun willst.


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Sa Nov 10, 2007 11:31 
Offline
DGL Member
Benutzeravatar

Registriert: Sa Jul 22, 2006 00:43
Beiträge: 30
Wohnort: Borland
Ok, vielen Dank für den kompetenten Rat. Dann werde ich wohl erst einmal bei TList mit einer Klasse bleiben.

:)


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Sa Nov 10, 2007 11:54 
Offline
DGL Member
Benutzeravatar

Registriert: Sa Dez 28, 2002 11:13
Beiträge: 2244
Wenn die Reihenfolge keine Rolle spielt, kann man auch in einer nicht verketteten Liste effizient löschen, indem man das zu löschende Element mit dem letzten vertauscht und dann das letzte Element löscht.

Zusätzlich kann man noch bei jedem Element (TShip) seinen Index in der Liste speichern. Dann geschieht das Löschen auch in konstanter Zeit.

Anstelle der TList wäre vielleicht die TObjectList interessant. Diese Liste gibt den Speicher der Objekte automatisch frei, wenn sie aus der Liste entfernt werden oder die TObjectList gelöscht wird.


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Sa Nov 10, 2007 19:42 
Offline
DGL Member
Benutzeravatar

Registriert: Sa Jul 22, 2006 00:43
Beiträge: 30
Wohnort: Borland
Hört sich gut an, danke. Eine Frage hätte ich allerdings noch:

Kann ich theoretisch die Instanz direkt in die Liste kreiren? Ansonsten müsste ich ja dafür auch nochmal ein Array anlegen.

Praktisch so:

Code:
  1.  
  2. var
  3.   List: TList;
  4. begin
  5.   List := TList.Create;
  6.  
  7.   List.Add(TShip.create);
  8.  
  9.   for Idx := 0 to List.Count -1 do
  10.     TShip(List[Idx]).Draw;
  11.  
  12.   List.Free;
  13. end;
  14.  


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Sa Nov 10, 2007 22:23 
Offline
DGL Member
Benutzeravatar

Registriert: Do Dez 05, 2002 10:35
Beiträge: 4234
Wohnort: Dortmund
Öhm. Ich habe zwar nicht ganz verstanden was du da genau wissen willst. Aber ja. Du kannst die Objekte auch direkt per Add hinzufügen. Also wie in deinem Code.


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Sa Nov 10, 2007 23:19 
Offline
DGL Member
Benutzeravatar

Registriert: Do Sep 02, 2004 19:42
Beiträge: 4158
Programmiersprache: FreePascal, C++
Ich benutze meist solche Konstrukte:
Code:
  1.  
  2.   with TShip(List.Items[List.Add(TShip.Create({evlt parameter}))]) do
  3.   begin
  4.     //...
  5.   end;
  6.  


Oder gebe eben den Wert zurück. TList.Add gibt ja praktischerweise den Index des hinzugefügten Items zurück.

Gruß Lord Horazont

_________________
If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung.
current projects: ManiacLab; aioxmpp
zombofant networkmy photostream
„Writing code is like writing poetry“ - source unknown


„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Sa Nov 10, 2007 23:47 
Offline
DGL Member
Benutzeravatar

Registriert: Do Dez 05, 2002 10:35
Beiträge: 4234
Wohnort: Dortmund
Ich muss gestehen, dass sich mir bei solchen Konstrukten die Fußnägel hochrollen. ;)

Also wenn man einen Wert doppelt braucht dann bevorzuge ich eine lokale Variable. Zu mal es auch übersichtlicher ist. Und solche vielfältige Verschachtelung versuche ich immer zu vermeiden.


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: Sa Nov 10, 2007 23:58 
Offline
DGL Member
Benutzeravatar

Registriert: Sa Dez 28, 2002 11:13
Beiträge: 2244
Da macht man sich dann besser eine eigene Funktion/Methode. Dann ist wenigstens immer sicher, dass ein Objekt in die Liste eingefügt wird.

Code:
  1. function AddShip:TShip;
  2. begin
  3.   result := TShip.Create;
  4.   List.Add(result);
  5. end;


Oder am besten gleich eine eigene TShipList. Die Generics in der nächsten Version von Delphi verringern den Schreibaufwand (http://video.codegear.com/RADStudioDevD ... nerics.zip).


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags:
BeitragVerfasst: So Nov 11, 2007 20:36 
Offline
DGL Member
Benutzeravatar

Registriert: Sa Jul 22, 2006 00:43
Beiträge: 30
Wohnort: Borland
Vielen Dank für die Ratschläge, wede es dann im Laufe der Woche mal umsetzen wenn ich Zeit finde ;)


Nach oben
 Profil  
Mit Zitat antworten  
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 14 Beiträge ] 
Foren-Übersicht » Programmierung » Allgemein


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 14 Gäste


Du darfst keine neuen Themen in diesem Forum erstellen.
Du darfst keine Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.
Du darfst keine Dateianhänge in diesem Forum erstellen.

Suche nach:
Gehe zu:  
  Powered by phpBB® Forum Software © phpBB Group
Deutsche Übersetzung durch phpBB.de
[ Time : 0.010s | 15 Queries | GZIP : On ]