Autor Beitrag
IHops
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 26

Vista
Delphi 7, Delphi 2007
BeitragVerfasst: 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 :?

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
destructor THaus.Destroy;
begin
  // Bild löschen
  with FContainer do
  begin
    Brush.Color := clWhite;
    Pen.Color := clWhite;
    Rectangle(FX-7,FY,FX+FWidth+7,FY-FHeight-2*FR-2);
  end;
  // Objekt entfernen
  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 ... :shock:

Hat jemand eine Idee, wo mein Denkfehler sitzt?
Xentar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2077
Erhaltene Danke: 2

Win XP
Delphi 5 Ent., Delphi 2007 Prof
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 26

Vista
Delphi 7, Delphi 2007
BeitragVerfasst: So 26.10.08 13:56 
ausblenden volle Höhe 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:
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 //Attribute
    FDoorOpen : boolean;
    FLightOn : boolean;
    FHeight : integer;
    FR : integer;
    FWidth : integer;
    FX : integer;
    FY : integer;
    FContainer : TCanvas;
    FRoofColor : TColor;
    FWallColor : TColor;

  private //Methoden
    procedure Delete; virtual;
    procedure DrawDoor; virtual;
    procedure DrawRoof; virtual;
    procedure DrawWall; virtual;
    procedure DrawWindow; virtual;

  public //Methoden
    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.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1876
Erhaltene Danke: 129

Win 8.1, Xubuntu 15.10

BeitragVerfasst: So 26.10.08 14:34 
Es wundert mich gerade selbst ein wenig, dass es nicht funktioniert. :nixweiss:
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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2889
Erhaltene Danke: 13

W2000, XP
D6E, BDS2006A, DevExpress
BeitragVerfasst: So 26.10.08 14:42 
Du solltest vielleicht den Destruktor als 'override' deklarieren.

_________________
Na denn, dann. Bis dann, denn.
Marc.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1876
Erhaltene Danke: 129

Win 8.1, Xubuntu 15.10

BeitragVerfasst: So 26.10.08 14:56 
user profile iconalzaimar hat folgendes geschrieben Zum zitierten Posting springen:
Du solltest vielleicht den Destruktor als 'override' deklarieren.

Daran habe ich bereits auch gedacht, dennoch funktioniert folgendes Konstrukt nicht so, wie es soll:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
type
  TMyClass = class
  public
     procedure Test;
     destructor Destroy; override;
  end;
...
destructor TMyClass.Destroy;
begin
  inherited Destroy;
end;
...
  with TMyClass.Create do
   try
     Test;
   finally
      Free;
      Test; // Wird ohne Fehler aufgerufen
   end;

Oder bin ich heute ganz neben der Kappe? :gruebel:
Hidden
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2242
Erhaltene Danke: 55

Win10
VS Code, Delphi 2010 Prof.
BeitragVerfasst: 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? :gruebel: 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.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1876
Erhaltene Danke: 129

Win 8.1, Xubuntu 15.10

BeitragVerfasst: So 26.10.08 15:25 
user profile iconHidden hat folgendes geschrieben Zum zitierten Posting springen:
Wie sieht denn die Prozedur 'Test' aus? les' oder schreib' mal Variablen.. Dürfte aber eigentlich schon so nicht gehen.

Aus Abstraktionsgründen, habe ich diese nicht mit ins Beispiel genommen. Beim eigentlichen Test habe ich in der Klasse TMyClass noch eine Feldvariable namens fZahl deklariert.
Die Prozedur Test sieht dann folgendermaßen aus:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TMyclass.Test;
begin
  fZahl := 3;
  ShowMessage(IntToStr(fZahl));
end;
IHops Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 26

Vista
Delphi 7, Delphi 2007
BeitragVerfasst: So 26.10.08 17:28 
Na, da bin ich erstmal beruhigt, dass ich es nicht als einziger nicht verstehe :roll: ... trotzdem vielen Dank schon mal für eure Mühe.

Hab mal weiter gesucht ...
ausblenden 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:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TObject.Free;
begin
  if Self <> nil then
    Destroy;
end;
alzaimar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2889
Erhaltene Danke: 13

W2000, XP
D6E, BDS2006A, DevExpress
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2242
Erhaltene Danke: 55

Win10
VS Code, Delphi 2010 Prof.
BeitragVerfasst: 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 user profile iconmatze: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 26

Vista
Delphi 7, Delphi 2007
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2242
Erhaltene Danke: 55

Win10
VS Code, Delphi 2010 Prof.
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 26

Vista
Delphi 7, Delphi 2007
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2684
Erhaltene Danke: 32



BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2598
Erhaltene Danke: 156

Ubuntu 13.04, Win 7
C# (VS 2013)
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 26

Vista
Delphi 7, Delphi 2007
BeitragVerfasst: Mo 27.10.08 10:04 
Also ein Haus wird durch Klick auf einen Button erstellt und auf die gewünschte Zeichenfläche gezeichnet:
ausblenden 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 ... :oops: ... muss es halt in Zukunft sorgfältiger tun :idea:
turboPASCAL
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 193
Erhaltene Danke: 1

Win XP / Vischda
D6 PE / D2005 PE
BeitragVerfasst: 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... :P
delfiphan
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2684
Erhaltene Danke: 32



BeitragVerfasst: Mo 27.10.08 11:50 
user profile iconIHops hat folgendes geschrieben Zum zitierten Posting springen:
...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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 26

Vista
Delphi 7, Delphi 2007
BeitragVerfasst: Mo 27.10.08 19:16 
user profile iconturboPASCAL hat folgendes geschrieben Zum zitierten Posting springen:
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!