![]() |
Variable als Zeiger?
Hallo,
ich arbeite zur Zeit an einem kleinem Schulprojekt, einer Ampelkreuzung-Simulation... Das Projekt ist Open Source und steht unter keinem Copyright. Es kann unter ![]() heruntergeladen werden (Code ist ausreichend kommentiert :D). Alles funktioniert so wie es soll, bis auf, dass wenn die Auto aus dem Sichtbarkeitsbereich (= der Form) verschwinden, diese auch zerstört werden sollen und der Speicher freigegeben werden soll... Folgendes Problem: Autos dürfen nicht aufeinanderfahren: Deshalb kennt jedes Auto, das Auto vor sich
Delphi-Quellcode:
Wenn kein Auto davor steht ist der Wert "nil"
procedure TAuto.SetzeAuto(pAuto:TAuto);
begin kAuto:=pAuto; end; Dann durch die Abfrage:
Delphi-Quellcode:
Wenn das Auto vor im "nil" ist fahre...
// Wenn Ampel nicht rot oder rot-gelb ist und kein Auto davor oder das Auto davor weit genug weg dann fahre!
procedure TAutoVonOben.Bewegen(Sender:TObject); begin if not (((kAmpel.GetStatus=1) or (kAmpel.GetStatus=2)) and (Top=150)) and ((kAuto=nil) or ((kAuto<>nil) and (kAuto.Top-Width-ABSTAND>Top))) then Top:=Top+1; end; Aber wenn ich während der Laufzeit ein Auto zerstöre und die Variale nil setze, bleibt der Wert kAuto.Top genauso wie vor dem Zerstören bzw. es ist garnicht "nil"... Ist es möglich, das kAuto keine Referenz oder Zeiger auf das Auto ist, sondern eine Kopie? Wie kann ich das Problem lösen? Ich wäre um jede Hilfe dankbar! greeetz neomic |
Re: Variable als Zeiger?
Also der Zeiger an sich ist einfach eine Zahl. Die Zahl gibt ja an, wo im Speicher sich die Information zum Auto befindet. Wenn du nun diesen Zeiger kopierst, dann wird nur der Wert kopiert und du hast zwei gleiche Werte,die auf denselben Speicher zeigen. Wenn du nun einen Wert auf 0 (also nil) setzt, dann bleibt der andere Zeiger unbeheligt davon.
Wenn du nun mehrere Zeiger auf das Auto hast und du verwendest einen Zeiger, um den Speicher des Autos zu löschen, dann hast du plötzlich mehrere Zeiger, die an die alte Stelle der Autoinformation zeigen. Jetzt ist der Speicher jedoch ungültig. Das führt zu Problemen (Merkwürdiges, AV). Es gibt eine Technik mit dem Namen Referenzzählung (Reference counting), die macht folgendes, um das Problem zu verhindern: Das Objekt (Auto) bekommt eine Integer Variable, die angibt, wieviele Zeiger auf das Objekt zeigen. Wenn diese Variable 0 ist, dann kann das Objekt gefahrlos gelöscht werden. Wenn es größer als 0 ist, dann darf es nicht gelöscht werden. Bei jedem Erzeugen eines Zeigers auf das Objekt, wird diese Variable um eines erhöht. Bei jedem Löschen (nil-setzen!!) eines Zeigers auf das Objekt, wird diese Variable um eins erniedrigt. Für Singlethread Programme wars das auch schon. Eine zweite Technik (deren Namen ich vergessen habe), funktioniert so: Es gibt nur eine Zeigervariable (Hauptzeiger), die direkt auf das Objekt zeigt. Alle neuen Zeiger zeigen auf diese Zeigervariable. Folgedessen muss diese Zeigervariable im Heap existieren (erzeugt durch GetMem oder New). Wenn man auf das Objekt zugreifen will, dass muss man zweimal referenzieren. Zuerst den Zeiger auf den Hauptzeiger und dann darüber auf das Objekt. Alle Zeiger zeigen eben zuerst auf den Hauptzeiger. Wenn man nun das Objekt löscht, dann setzt man den Hauptzeiger auf nil (nicht FreeMem der Dispose machen!) und alle andere Zeiger zeigen dann auf nil. Natürlich muss man diese Hauptzeiger auch irgendwann mal löschen. Mann kann sie mit der Referenzählung verwalten oder in einer List verwalten, die dann ganz zum Schluss gelöscht wird. |
Re: Variable als Zeiger?
Kann ich den Wert in dem Speicher nil setzen?
Ansonsten versteh ich das mit deiner vorgschlagenen Methode noch nicht so ganz: Was muss ich der Variable den zuweisen bzw wie geht das überhaupt? |
Re: Variable als Zeiger?
Das Problem steckt sieht doch so vereinfacht aus:
Delphi-Quellcode:
var obj1, obj2 : TAuto;
begin obj1 := TAuto.Create; obj2 := obj1; //obj2 = obj1 - obj1 und obj2 zeigen auf dasselbe Auto im Speicher //sind jedoch zwei verschiedene variablen, eben nur mit demselben Inhalt obj1.Fahre; //ok obj2.Fahre; //ok obj1.Free; obj1 := nil; if Assigned(obj1) then //False obj1.Fahre; if Assigned(obj2) then //true obj2.Fahre; //AV end; |
Re: Variable als Zeiger?
gut, soweit habe ich das jetzt verstanden...
aber wie kann ich es erreichen, das wenn das vorherige Auto zerstört wird auch die Abfrage kAuto=nil zutrifft? |
Re: Variable als Zeiger?
Eine zweite Technik (deren Namen ich vergessen habe), funktioniert so:
Es gibt nur eine Zeigervariable (Hauptzeiger), die direkt auf das Objekt zeigt. Alle neuen Zeiger zeigen auf diese Zeigervariable. Folgedessen muss diese Zeigervariable im Heap existieren (erzeugt durch GetMem oder New). Wenn man auf das Objekt zugreifen will, dass muss man zweimal referenzieren. Zuerst den Zeiger auf den Hauptzeiger und dann darüber auf das Objekt. Alle Zeiger zeigen eben zuerst auf den Hauptzeiger. Wenn man nun das Objekt löscht, dann setzt man den Hauptzeiger auf nil (nicht FreeMem der Dispose machen!) und alle andere Zeiger zeigen dann auf nil. Natürlich muss man diese Hauptzeiger auch irgendwann mal löschen. Mann kann sie mit der Referenzählung verwalten oder in einer List verwalten, die dann ganz zum Schluss gelöscht wird. |
Re: Variable als Zeiger?
Am einfachsten zu realisieren wäre wohl, dass jedes Auto von seinem vorausfahrenden Kollegen vor dessen Freigabe durch ein Event benachrichtigt wird und so sein Feld auf nil setzen kann. Man könnte der Auto-Klasse natürlich statt dem Event auch eine Referenz auf das hinterherfahrende Auto geben, aber solange kein Rückspiegel eingebaut wird ^^, ist diese Information ansonsten unnütz und das Event damit sauberer.
|
Re: Variable als Zeiger?
Aktuell habe ich eine (recht) einfache Möglichkeit gefunden:
Alles spielt sich jetzt eigentlich nur in der zweiten for-Schleife ab:
Delphi-Quellcode:
Der Code müsste von der Logik her richtig sein, aber ich bekomme nen RangeCheck Error (keine AV mehr *juhu*)
procedure TSteuerung.Aufraeumen();
var i,h,j:Integer; begin for i:=0 to 3 do for h:=0 to (Length(hAuto[i])-1) do begin if hAuto[i][h].IstAusDemBild then begin // Auto zerstören hAuto[i][h].Destroy; // Array Indizes verschieben und Array verkürzen for j:=h to (Length(hAuto[i])-2) do hAuto[i][j]:=hAuto[i][j+1]; SetLength(hAuto[i],Length(hAuto[i])-2); // Vordermann löschen if h>0 then hAuto[i][h-1].SetzeAuto(nil); end; end; end; Einer ne Idee? :) |
Re: Variable als Zeiger?
Hi,
Zitat:
Warum arbeitest du eigentlich mit dynamischen Arrays? Delphi bietet mit ![]() Die Freigabe eines Objekts sollte mit der Methode ![]() Die Funktion ![]() Gruß Hawkeye |
Re: Variable als Zeiger?
Ich finde die Idee gut...
Aber ich habe noch nen paar Probleme mit dem umsetzen:
Delphi-Quellcode:
Der erkennt hAuto[pArt].Last nicht als TAuto ("Undeclared identifier: 'SetzeAuto'")
procedure TSteuerung.NeuesAuto(pArt:Integer);
var Auto:TAuto; begin // Auto erstellen case pArt of 0:Auto:=TAutoVonUnten.Create(kForm,hAmpel[pArt]); 1:Auto:=TAutoVonRechts.Create(kForm,hAmpel[pArt]); 2:Auto:=TAutoVonOben.Create(kForm,hAmpel[pArt]); 3:Auto:=TAutoVonLinks.Create(kForm,hAmpel[pArt]); end; // Auto zuweisen hAuto[pArt].Add(Auto); // Vordermann zuweisen if hAuto[pArt].Count>0 then hAuto[pArt].Last.SetzeAuto(hAuto[pArt].Items[hAuto[pArt].Count-1]) else hAuto[pArt].Last.SetzeAuto(nil); end; Was kann ich tun? edit: Oh ich glaub ich hab vergessen zu casten :D edit2: Soweit ans laufen bekommen, nur das SetzeAuto immer nil zuweißt...
Delphi-Quellcode:
procedure TSteuerung.NeuesAuto(pArt:Integer);
var Auto,Auto2:TAuto; var i:Integer; begin // Auto erstellen case pArt of 0:Auto:=TAutoVonUnten.Create(kForm,hAmpel[pArt]); 1:Auto:=TAutoVonRechts.Create(kForm,hAmpel[pArt]); 2:Auto:=TAutoVonOben.Create(kForm,hAmpel[pArt]); 3:Auto:=TAutoVonLinks.Create(kForm,hAmpel[pArt]); end; // Auto zuweisen i:=hAuto[pArt].Add(Auto); // Vordermann suchen if i>0 then Auto2:=TAuto(hAuto[pArt].Items[i-1]) else Auto2:=nil; // Vordermann zuweisen TAuto(hAuto[pArt].Items[i]).SetzeAuto(Auto2); end; Sieht einer den Fehler? o.0 |
Re: Variable als Zeiger?
Ich habe herausgefunden, dass Add() mir immer "0" zurückgibt bzw. count immer 1 bleibt... Was soll das denn?
Eigentlich sollte es doch den Index zurückgeben an dem es in der Liste steht, oder? |
Re: Variable als Zeiger?
Tut es ja.
Der Index des ersten Objekts (count=1) ist 0. |
Re: Variable als Zeiger?
Hallo,
wenn dir Add() immer den Wert 0 liefert, dann war die Liste vor dem Hinzufügen des neuen Elements offenbar leer. Vielleicht hast du einen logischen Fehler in deinem Programm und erzeugst immer neue Listen anstatt die bereits existierenden zu verwenden. Gruß Hawkeye |
Re: Variable als Zeiger?
Zitat:
OMG JA NOCHMAL!!!! Guckt mal wie ich nen neues Auto mache -.- (per Button)
Delphi-Quellcode:
ich danke dir!
procedure TSteuerung.Einchalten();
var i:Integer; begin // Variablen setzen Randomize; hTimer.Enabled:=True; // Wir haben 4 verschiedene Autoarten (Also für jede Autoart ein Unterarray) [b] for i:=0 to 3 do hAuto[i]:=TObjectList.Create(); NeuesAuto(3);[/b] end; Versuch das jetzt noch mit dem Löschen hinzubekommen... Dazu eine Frage: Wird Destroy/Free dann automatisch aufgerufen bei Remove? Und wenn Free aufgerufen wird, muss ich dann auch in TAuto Free überschreiben oder ruft der dann das Destroy von der untersten Klasse auf? |
Re: Variable als Zeiger?
Ich kann hier nur die Hilfe zitieren:
Zitat:
Gruß Hawkeye |
Re: Variable als Zeiger?
Ich hab mir da jetzt mal sowas gebastelt:
Delphi-Quellcode:
Ich bekomm wieder ne AV, wahrscheinlich (wie vorher schon gesagt), weil hAuto[i].Count in der while-Schleife nur einmal am Anfang aufgenommen wird und sich dieser Wert bei den nächsten Schleifendurchläufen nicht ändert...
procedure TSteuerung.Aufraeumen();
var i,h:Integer; begin for i:=0 to 3 do begin h:=0; while h<hAuto[i].Count do begin if TAuto(hAuto[i].Items[h]).IstAusDemBild then begin // Auto aus der Liste entfernen hAuto[i].Delete(h); // Aus der Variable vom Hintermann löschen wenn vorhanden if h>0 then TAuto(hAuto[i].Items[h-1]).SetzeAuto(nil); end else h:=h+1; end; end; end; Kann ich mir anders helfen? |
Re: Variable als Zeiger?
So für alle die es interessiert: Ich hab es endlich geschafft :)
Und zwar frag jetzt jedes Auto selber ab ob es gelöscht werden kann und stellt einen Löschantrag an die Steeuerung. Diese sieht jetzt so aus:
Delphi-Quellcode:
Danke nochmal an alle beteiligten!
procedure TSteuerung.Loeschen(Sender:TObject);
var i:Integer; begin i:=-1; // Typ abfragen if Sender is TAutoVonUnten then i:=0 else if Sender is TAutoVonRechts then i:=1 else if Sender is TAutoVonOben then i:=2 else if Sender is TAutoVonLinks then i:=3; if i>=0 then begin // Auto löschen und aus der Variable des Hintermannes löschen TAuto(hAuto[i].Extract(Sender)).Destroy; // Items[0] ist zwingend der erste Autofahrer nach der andere gelöscht ist if hAuto[i].Count>0 then TAuto(hAuto[i].Items[0]).SetzeAuto(nil); end; end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:41 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