| Autor |
Beitrag |
IHops
      
Beiträge: 26
Vista
Delphi 7, Delphi 2007
|
Verfasst: So 26.10.08 00:16
Ich habe für ein kleines "Schulprogramm" eine Klasse THaus erstellt. Objekte Haus werden dann gezeichnet, können verschoben werden ... durch Klick auf einen Button wird das Objekt auch wieder zerstört - sollte es zumindest
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| destructor THaus.Destroy; begin with FContainer do begin Brush.Color := clWhite; Pen.Color := clWhite; Rectangle(FX-7,FY,FX+FWidth+7,FY-FHeight-2*FR-2); end; inherited Destroy; end; |
Nunja, das Bild entfernen funktioniert, nur das Objekt an sich ist danach immer noch da. Sobald ich mit Hilfe der Scrollbars das Haus (welches eigentlich nicht mehr da ist) verschiebe, wird es wieder gezeichnet ...
Hat jemand eine Idee, wo mein Denkfehler sitzt?
|
|
Xentar
      
Beiträge: 2077
Erhaltene Danke: 2
Win XP
Delphi 5 Ent., Delphi 2007 Prof
|
Verfasst: So 26.10.08 00:32
Hm.. zeig mal, die THaus aufgebaut ist, also das Interface davon.
Edit:
BTW: Wenn du das Objekt zerstören willst, brauchst du kein Rechteck mehr zu malen, da es danach ja sowieso weg ist und wieder vom Formular übermalt wird.
_________________ PROGRAMMER: A device for converting coffee into software.
|
|
IHops 
      
Beiträge: 26
Vista
Delphi 7, Delphi 2007
|
Verfasst: So 26.10.08 13:56
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44:
| UNIT mTHaus;
interface
uses Graphics, Types;
type THaus = class
private FDoorOpen : boolean; FLightOn : boolean; FHeight : integer; FR : integer; FWidth : integer; FX : integer; FY : integer; FContainer : TCanvas; FRoofColor : TColor; FWallColor : TColor;
private procedure Delete; virtual; procedure DrawDoor; virtual; procedure DrawRoof; virtual; procedure DrawWall; virtual; procedure DrawWindow; virtual;
public constructor Create (x: integer; y: integer; w: integer; rc: tColor; wc: TColor; Container: TCanvas); virtual; procedure CloseDoor; virtual; procedure DrawHouse; virtual; procedure DrawNew (x: integer; y: integer; w: integer; rc: tColor; wc: TColor); virtual; procedure LightOff; virtual; procedure LightOn; virtual; procedure OpenDoor; virtual; function IsDoorOpen : boolean; virtual; function IsLightOn : boolean; virtual; destructor Destroy; virtual;
end;
implementation |
x, y und w sind Parameter für das Haus, rc und wc Dach- bzw. Wandfarbe. Das Haus wird mit DrawHouse auf der gewünschten Zeichenfläche gezeichnet. Im Grunde müsste ich beim Zerstören dann auch dafür sorgen, dass das Haus von der Zeichenfläche verschwindet. Nur müsste - meinem Verständnis nach - das Objekt Haus nach dem Aufruf von destroy nicht mehr verfügbar sein, d. h. ein DrawNew müsste z. B. zu einer Fehlermeldung führen ... aber das tuts nicht. Hab ich da ein fundamentales Verständnisproblem?
|
|
Marc.
      
Beiträge: 1876
Erhaltene Danke: 129
Win 8.1, Xubuntu 15.10
|
Verfasst: So 26.10.08 14:34
Es wundert mich gerade selbst ein wenig, dass es nicht funktioniert.
Was aber funktionieren sollte, wäre FreeAndNil(Object); anstelle von Destroy.
€: Was aber scheinbar auch nicht der Fall ist. Lediglich die Referenz wird NIL gesetzt, das Objekt bleibt bestehen...
Grüße,
Marc
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: So 26.10.08 14:42
Du solltest vielleicht den Destruktor als 'override' deklarieren.
_________________ Na denn, dann. Bis dann, denn.
|
|
Marc.
      
Beiträge: 1876
Erhaltene Danke: 129
Win 8.1, Xubuntu 15.10
|
Verfasst: So 26.10.08 14:56
|
|
Hidden
      
Beiträge: 2242
Erhaltene Danke: 55
Win10
VS Code, Delphi 2010 Prof.
|
Verfasst: So 26.10.08 15:19
Wie sieht denn die Prozedur 'Test' aus? les' oder schreib' mal Variablen.. Dürfte aber eigentlich schon so nicht gehen.
Vielleicht optimiert der Compiler das Objekt weg?  Kann ich mir aber eignetlich auch nicht vorstellen.
_________________ Centaur spears can block many spells, but no one tries to block if they see that the spell is a certain shade of green. For this purpose it is useful to know some green stunning hexes. (HPMoR)
|
|
Marc.
      
Beiträge: 1876
Erhaltene Danke: 129
Win 8.1, Xubuntu 15.10
|
Verfasst: So 26.10.08 15:25
|
|
IHops 
      
Beiträge: 26
Vista
Delphi 7, Delphi 2007
|
Verfasst: So 26.10.08 17:28
Na, da bin ich erstmal beruhigt, dass ich es nicht als einziger nicht verstehe  ... trotzdem vielen Dank schon mal für eure Mühe.
Hab mal weiter gesucht ...
Delphi-Quelltext 1: 2: 3:
| destructor TObject.Destroy; begin end; |
... so sieht der Destruktor des TObject in Delphi aus ... das erklärt zumindest, dass nix passiert. Allerdings kann ich nicht verstehen, weshalb mit Destroy Objekte nicht gelöscht werden können ...
In der Hilfe steht dazu: "Rufen Sie System::TObject::Destroy nicht direkt auf. Verwenden Sie stattdessen System::TObject::Free. Die Methode Free überprüft, ob die Objekt-Referenz nicht bereits nil ist und ruft System::TObject::Destroy nur bei Bedarf auf." Nun ja, das hab ich gemacht - mit dem Ergebnis, dass das Programm nun gleich beim Aufruf von Free abstürzt, obwohl ich ein try ... except verwende und das Free das eigentlich auch vorher abtesten dürfte:
Delphi-Quelltext 1: 2: 3: 4: 5:
| procedure TObject.Free; begin if Self <> nil then Destroy; end; |
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: So 26.10.08 17:34
Es ist vollkommen normal, das du eine Methode aufrufen kannst, nachdem das Objekt freigegeben wurde, warum auch nicht. Der Code existiert ja unabhängig vom Objekt. Du bekommst erst dann Probleme, wenn Du innerhalb der Methode auf Klassenfelder zugreifst, weil dann dieser Speicherbereich anderweitig vergeben sein könnte.
_________________ Na denn, dann. Bis dann, denn.
|
|
Hidden
      
Beiträge: 2242
Erhaltene Danke: 55
Win10
VS Code, Delphi 2010 Prof.
|
Verfasst: So 26.10.08 18:06
Hi,
.Free hat einen Sicherungsschalter, falls das Objekt nicht existiert, das hast du schon richtig erkannt. Dieser funktioniert allerdings nur, wenn das Objekt nil ist(das Speicherreferenz-Symbol für "leer").
Deshalb sollten Objekte nur mit FreeAndNil(MyObject) freigegeben werden. Ferner muss eine Variable initialisiert werden. Das heißt, bei der Initialisierung deiner Anwedung solltest du einen Wert hineinschreiben. Entweder ist das z.B. MyInteger := 0; bei gewöhnlichen Variablen(deren Freigabe du nicht selbst übernehmen musst), MyReference := TMyClass.Create(Params); oder, falls du es nicht direkt erzeugen willst, MyReference := nil;.
Zu try: Das liegt an der IDE(bei dir Delphi?). Die Fehlermeldungen treten auf und werden programmintern behandelt. Delphi erkennt sie aber isoliert nocheinmal und sorgt dafür, dass sie dir trotzdem angezeigt werden. Und das ist auch gut so. Das ignorieren von Fehlermeldungen ist nämlich so ziemlich des Pudels Kern, wenn ich das mal so sagen darf
mfG,
Moderiert von matze: Delphi-Tags hinzugefügt
_________________ Centaur spears can block many spells, but no one tries to block if they see that the spell is a certain shade of green. For this purpose it is useful to know some green stunning hexes. (HPMoR)
|
|
IHops 
      
Beiträge: 26
Vista
Delphi 7, Delphi 2007
|
Verfasst: So 26.10.08 19:17
Hm, das mit dem Haus:= nil hab ich ja dann auch gemacht ... damit geht es auch. Nach meinem Verständnis sollte aber gerade die Methode Destroy dieses alles erledigen ... zumindest hatte ich das mal so gelernt. Und nun sehe ich, dass Destroy eigentlich gar nichts mehr macht ...
|
|
Hidden
      
Beiträge: 2242
Erhaltene Danke: 55
Win10
VS Code, Delphi 2010 Prof.
|
Verfasst: So 26.10.08 19:36
Hi,
Das ist so nicht ganz richtig. Die eigentliche Freigabe des Speichers geschieht über Destroy(das steht da nur nicht drin, sondern macht imho der Compiler automatisch. Deshalb wird die Methode ja gesondert bezeichnet - "destructor".
Free ruft ja auch nur destroy auf, insofern wird destroy nie verwendet. Es wird immer nur geFreet, jede Verwendung ist implizit.
Das mit dem Nil hat aber garnichts mit dem Objekt zu tun, sondern mit der Objektreferenz. Das ist praktisch nur ein Integer-Wert, in dem steht, wo das Objekt zu finden ist.
Deshalb kann FreeAndNil auch keine Methode des objekts sein - es hat garncihts mit ihm zu tun. Sonst würde das wahrscheinlich schon mit .Free erledigt.
mfG,
_________________ Centaur spears can block many spells, but no one tries to block if they see that the spell is a certain shade of green. For this purpose it is useful to know some green stunning hexes. (HPMoR)
|
|
IHops 
      
Beiträge: 26
Vista
Delphi 7, Delphi 2007
|
Verfasst: So 26.10.08 21:09
Wenn ich mal zu einer Schlussfolgerung kommen soll:
(1)
Haus.Destroy benötige ich nur, um das Haus von der Bildfläche zu löschen (in diesem Fall ein rein optisches Problem) ...
(2)
... rufe Haus.Destroy aber nie auf, sondern Haus.Free, damit wird der Destructor (bei Bedarf) automatisch aufgerufen.
(3)
Um das Haus komplett rauszuwerfen, setze ich die Referenz mit Haus:= nil ins Nirvana ...
|
|
delfiphan
      
Beiträge: 2684
Erhaltene Danke: 32
|
Verfasst: So 26.10.08 21:25
Die Unterscheidung (1) und (3) gibt es nicht. Es gibt nicht verschiedene Arten von Freigaben. Eine für das "Löschen von der Bildfläche" und die andere fürs "komplette Rauswerfen". Entweder ist das Objekt da, oder eben nicht. Solange es existiert, darfst du darauf zugreifen. Sobald es freigegeben ist, nicht mehr. Es kann eine Hilfe sein, die Referenz nach der Freigabe auf nil zu setzen. Dann kriegst du bei einem allfälligen Zugriff auf Felder eine Fehlermeldung, und ein erneuter Freigabeversuch über Free wird ignoriert.
Dass du nach einem Free oder Destroy immer noch Methoden des Objekts aufrufen kannst, ist nicht verwunderlich. Eine Methode ist vergleichbar mit einer Prozedur, die "this" als ersten Parameter hat. Ob "this" noch gültig ist oder nil, ist für den Aufruf egal. Erst wenn du darauf zugreifst gibt es Probleme, weil der Speicherblock entweder schon freigegeben wurde, anderweitig verwendet wird, oder eben nil ist. Kurz: Ein Zugriff aufs Objekt nach dem Freigeben ist ungültig, wird aber nicht zwingend mit einer Fehlermeldung quittiert. Du musst den Code selbst so gestalten, dass ein solcher Zugriff nie passiert.
|
|
Yogu
      
Beiträge: 2598
Erhaltene Danke: 156
Ubuntu 13.04, Win 7
C# (VS 2013)
|
Verfasst: So 26.10.08 22:29
Wie kommt es eigentlich, dass die Zeichenmehtode immer noch aufgerufen wurde, obwohl du das Objekt eigentlich löschen willst? So wie deine Klassendeklaration aussieht (ist ja nicht von TControl abgeleitet), rufst du die Methode DrawHouse manuell auf. Vor diesen Aufruf sollte doch noch eine Methode, die überprüft, ob überhaupt ein Haus existiert.
Wie sieht denn dieser Aufruf aus? Poste doch mal etwas Quelltext um diesen Bereich.
|
|
IHops 
      
Beiträge: 26
Vista
Delphi 7, Delphi 2007
|
Verfasst: Mo 27.10.08 10:04
Also ein Haus wird durch Klick auf einen Button erstellt und auf die gewünschte Zeichenfläche gezeichnet:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26:
| procedure TFrmMain.BtnCreateHouseClick(Sender: TObject); var x, y, b : integer; rc, wc : TColor; begin GetParameter(x,y,b,rc,wc); Haus:= THaus.Create(x,y,b,rc,wc,FrmMain.Canvas); Haus.DrawHouse; end;
...
constructor THaus.Create (x: integer; y: integer; w: integer; rc: tColor; wc: TColor; Container: TCanvas); begin inherited Create; FX := x; FY := y; FWidth := w; FR := FWidth div 5; FHeight := FWidth; FLightOn := false; FDoorOpen := false; FRoofColor := rc; FWallColor := wc; FContainer := Container; end; |
@delfiphan: Das es keine Unterscheidung zwischen (1) und (3) gibt war ja auch meine (ursprünglich) Meinung, nur sorgt das Destroy eben nicht dafür, dass auch die Referenz entfernt wird; der Zeiger Haus weist immer noch auf die korrekte Stelle im Speicher. So passiert es mir halt, dass auch nach einem Aufruf von Destroy noch gezeichnet werden kann, obwohl es nicht mehr da ist. Eine mögliche Erklärung kann sein, dass der Speicher nicht wirklich freigegeben bzw. anderweitig verwendet wird, so dass an der entsprechenden Adresse halt immer noch "brauchbare" Daten stehen.
Schlussfolgerung: Ich hab meine Objekte (bisher) nur unzureichend freigegeben ...  ... muss es halt in Zukunft sorgfältiger tun 
|
|
turboPASCAL
      
Beiträge: 193
Erhaltene Danke: 1
Win XP / Vischda
D6 PE / D2005 PE
|
Verfasst: Mo 27.10.08 10:13
Wie oft erstellst du denn das Haus ? Mehrmals ?
Kannst du mal den gesammten Quelltext (als Zip) anhängen ?
_________________ Nein, ich bin nicht der turboPASCAL aus der DP, ich seh nur so aus...
|
|
delfiphan
      
Beiträge: 2684
Erhaltene Danke: 32
|
Verfasst: Mo 27.10.08 11:50
IHops hat folgendes geschrieben : | | ...der Zeiger Haus weist immer noch auf die korrekte Stelle im Speicher. So passiert es mir halt, dass auch nach einem Aufruf von Destroy noch gezeichnet werden kann, obwohl es nicht mehr da ist. Eine mögliche Erklärung kann sein, dass der Speicher nicht wirklich freigegeben bzw. anderweitig verwendet wird, so dass an der entsprechenden Adresse halt immer noch "brauchbare" Daten stehen. |
Ich glaube du meintest das richtige, hat aber evtl. zu Missverständnissen geführt. Ich würde nicht sagen, dass "Haus" auf die "korrekte" Stelle im Speicher zeigt, sondern auf die gleiche(, aber ungültig gewordene) Stelle.
Deine Erklärung ist richtig. Bei der Freigabe kleiner Speicherblöcke passiert beim Delphi Memory Manager mit dem eigentlichen Speicherblock meistens nichts. Sondern es wird lediglich die Buchhaltung angepasst, dass die Stelle jetzt frei ist und wieder belegt werden kann. Dabei wird die Stelle selbst meist weder physikalisch freigegeben noch geräumt noch gelöscht, sodass ein Zugriff nicht zwingend zu einem Fehler führt. Die Stelle könnte aber schon im nächsten Augenblick mit etwas anderem überschrieben oder physikalisch freigegeben werden.
|
|
IHops 
      
Beiträge: 26
Vista
Delphi 7, Delphi 2007
|
Verfasst: Mo 27.10.08 19:16
turboPASCAL hat folgendes geschrieben : | | Wie oft erstellst du denn das Haus ? Mehrmals ? |
Das Haus wird nur 1x erstellt, d.h. Haus.Create wird nur 1x aufgerufen (zumindest ist es so gedacht - wird aber programmtechnisch nicht konkret erzwungen). Mit Haus.Draw kann es nur beliebig oft gezeichnet werden.
Einloggen, um Attachments anzusehen!
|
|