Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Zeiger freigeben (https://www.delphipraxis.net/47826-zeiger-freigeben.html)

TomDooley 16. Jun 2005 19:42


Zeiger freigeben
 
Hallo

Beim Freigeben von Zeiger habe ich nicht ganz den Durchblick. Gegeben ist folgender Code (von zwei Klassen):

Delphi-Quellcode:
function TTestClassA.getListe: TList;
var
  List : TList;
  PTestRecord : ^TTestRecord;
begin
  new(PTestRecord);
  List := TList.Create;
  PTestRecord^.FeldA := 'Hallo';
  PTestRecord^.FeldB := 'Test';
  PTestRecord^.FeldC := 1;
  List.Add(PTestRecord);
  Result := List;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  List : TList;
  TestClassA : TTestClassA;
  PTestRecord : ^TTestRecord;
begin
  TestClassA := TTestClassA.Create;
  List := TestClassA.getListe;
  PTestRecord := List.Items[0];
  Label1.Caption := PTestRecord^.FeldA;
  Dispose(PTestRecord);
end;
Soll ich nun den Zeiger wie ich's gemacht habe in der zweiten Funktion disposen oder macht man in solchen Fällen in der TestClassA eine Prozedur, die dann einfach dispose(PTestRecord) aufruft (und in der Button1Click-Prozedur aufgerufen wird)?
Die selbe Frage stellt sich natürlich auch wenn man statt mit TList mit TObjectList arbeiten würde (und dann Objekte auflistet). Dann würde aber nur der Weg über eine Funktion der Klasse TTestClassA funtktionieren, oder?

Ich wäre froh wenn mir jemand den "Standardweg" aufzeigen könnte.

Danke und Gruss

Tom

DP-Maintenance 16. Jun 2005 19:44

DP-Maintenance
 
Dieses Thema wurde von "alcaeus" von "Programmieren allgemein" nach "Object-Pascal / Delphi-Language" verschoben.
Ist eine Frage zu Object Pascal

Muetze1 16. Jun 2005 22:14

Re: Zeiger freigeben
 
Moin!

Zitat:

Zitat von TomDooley
Soll ich nun den Zeiger wie ich's gemacht habe in der zweiten Funktion disposen oder macht man in solchen Fällen in der TestClassA eine Prozedur, die dann einfach dispose(PTestRecord) aufruft (und in der Button1Click-Prozedur aufgerufen wird)?

Nach dem Dispose() zeigt aber der erste Eintrag der Liste ins Nirvana, da du den Speicherbereich freigegeben hast, wo der Record liegt und wo der Eintrag der Liste hinzeigt. Die Liste gibt dir ja nur einen Pointer zurück auf die Stelle im Speicher wo der Record liegt. Nun musst du aber den reinen Pointer den du beim Button Click bekommst aber nicht freigeben, da es wie eine Integer Variable ist - und die musste auch nicht freigeben.

Grundsätzlich musst du den Speicher für den Record freigeben, wenn du den Eintrag löscht in der Liste - weil die Liste verwaltet nur Zeiger, die zweiss nix von dem Typ und kann daher auch nicht den Speicher freigeben.

Zitat:

Zitat von TomDooley
Die selbe Frage stellt sich natürlich auch wenn man statt mit TList mit TObjectList arbeiten würde (und dann Objekte auflistet). Dann würde aber nur der Weg über eine Funktion der Klasse TTestClassA funtktionieren, oder?

Da ist es ja so, das du auch nur eine Referenz auf das Objekt bekommst. Würdest du das Objekt freigeben, dann zeigt der Eintrag der Objektliste an der Stelle ins Nirvana, weil das Objekt weg ist. Daher würde ein späterer Zugriff auf das Objekt krachen.

Ausserdem hat die TObjectlist die einfache Möglichkeit das man ihr sagen kann (ist sogar Standardeinstellung ->OwnsObjects), dass sie beim Entfernen eines Objektes aus der Liste, dieses auch freigibt (->Delete()). Wenn dies nicht gewünscht ist, dann kann man auch Remove() nutzen, welches das Objekt zurück gibt, den Eintrag aus der Liste entfernt aber das Objekt nicht freigibt.

MfG
Muetze1

TomDooley 17. Jun 2005 10:15

Re: Zeiger freigeben
 
Tag auch

Danke erstmal für die Infos. Vielleicht erst zu den Zeigern:

Das Dispose in Button1Click ist demzufolge unnötig. Wie und vor allem wann müsste aber nun in meinem Beispiel Dispose aufgerufen werden? Sehe ich das zudem richtig dass ich mit TList ein Speicherleck geschaffen habe (List wird in getListe nicht zerstört, darf aber auch nicht sein da ansonsten in Button1Click nicht mehr verfügbar --> wann und wie soll ich List freigeben?).

Ich wäre froh wenn jemand mein Bespiel so ändern könnte, das alles richtig funktioniert und schlussendlich richtig freigegeben wird.

Danke und Gruss

Tom

TomDooley 20. Jun 2005 12:19

Re: Zeiger freigeben
 
Ich hab's mal selbst probiert:

Delphi-Quellcode:
function TTestClassA.getListe: TList;
var
  List : TList;
begin
  new(PTestRecord);
  List := TList.Create;
  PTestRecord^.FeldA := 'Hallo';
  PTestRecord^.FeldB := 'Test';
  PTestRecord^.FeldC := 1;
  List.Add(PTestRecord);
  Result := List;
  List.Free;
end;

procedure TTestClassA.Destroy;
begin
  Dispose(PTestRecord);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  List : TList;
  TestClassA : TTestClassA;
  PTestRecordKopie : ^TTestRecord;
begin
  TestClassA := TTestClassA.Create;
  List := TestClassA.getListe;
  PTestRecordKopie := List.Items[0];
  Label1.Caption := PTestRecordKopie^.FeldA;
  List.Free;
  TestClassA.Destroy;
end;
PTestRecord in TTestClassA müsste als Membervariable von TTestClassA definiert werden damit sie in Destroy zerstört werden kann.
Stimmt das so?

Danke und Gruss

Tom

Khabarakh 20. Jun 2005 12:56

Re: Zeiger freigeben
 
  • Sollte PTestRecord nicht eher im Konstruktor "gefüllt" werden?
  • Destroy ist keine normale Prozedur, sondern ein Destruktor. Man sollte immer den Destruktor der Basisklasse überschreiben und am Ende inherited aufrufen:
    Delphi-Quellcode:
    destructor TTestClassA.Destroy;
    begin
      Dispose(PTestRecord);
      inherited;
    end;
  • Delphi-Quellcode:
    TestClassA.Destroy;
    Du solltest immer Free statt Detroy zum Freigeben einer Instanz aufrufen. Wenn du den Destruktor wie oben beschrieben korrigierst, wird bei Free auch dein eigener aufgerufen.
  • Delphi-Quellcode:
    Result := List;
      List.Free;
    :gruebel: ?
  • Nimm doch statt der Prozedur GetList eine read-only Property.

TomDooley 20. Jun 2005 13:20

Re: Zeiger freigeben
 
Zitat:

Sollte PTestRecord nicht eher im Konstruktor "gefüllt" werden?
Ich gehe davon aus, dass der Speicher bei Bedarf reserviert wird und nach der "Verarbeitung" gleich wieder freigegeben wird (siehe nächste Anmerkung).

Zitat:

Destroy ist keine normale Prozedur, sondern ein Destruktor.
Ich weiss, ich wollte damit nur demonstrieren, dass der Speicher freigegeben wird (so gesehen eine etwas schlechte Namenswahl von mir). Diese Prozedur wird also nicht beim Freigeben des Objekts sondern unmittelbar nach dem "Benutzen" von PTestRecord aufgerufen.

Zitat:

Nimm doch statt der Prozedur GetList eine read-only Property
Danke für den Tip. Es geht hier in erster Linie aber darum, wie mit Zeigern umgegangen werden soll.

TomDooley 21. Jun 2005 13:25

Re: Zeiger freigeben
 
Hallo alle,

das mit dem Freigeben vom Listenobjekt habe ich inzwischen so gelöst, indem ich die Liste der function getListe als var-Parameter übergebe. Es bleibt aber immer noch die Frage, wo und wann ich den mit new(PTestRecord) reservierten Speicher freigeben kann/soll... (ich denke ich kann new(PTestRecord) nicht im Konstruktor aufrufen da ich nicht weiss wie oft die Funktion getListe nacheinander aufgerufen wird)

Danke und Gruss
Tom

runger 21. Jun 2005 13:34

Re: Zeiger freigeben
 
Hallo,

ein kurzer Kommentar:

einen Zeiger kann man nicht freigeben, höchstens reservierten Speicher!
Ein Zeiger ist nichts als eine Adresse.

Rainer

Robert_G 21. Jun 2005 14:07

Re: Zeiger freigeben
 
Warum willst du unbedingt it records und Zeigern arbeiten?

Alki hat in der CodeLib ein Hier im Forum suchenObjectList template platziert.
Damit ist folgendes möglich:

Eine Klasse, die dein Item darstellt (die Liste wird durch ein paar Extra runden des Compilers angelegt ;) ):
Delphi-Quellcode:
uses Contnrs;

type
   TTest = class
   private
      fSomeValue: Integer;
   public
      property SomeValue: Integer read fSomeValue;
      procedure DoSomething;
      constructor Create(aSomeValue :Integer); virtual;
   end;

   _LIST_ITEM_ = TTest;
{$DEFINE TYPED_LIST_TEMPLATE}
{$INCLUDE ObjList_Tmpl.pas}
   TTestList = _LIST_;

implementation

{$INCLUDE ObjList_Tmpl.pas}

{ TestClass }

constructor TTest.Create(aSomeValue: Integer);
begin
   fSomeValue := aSomeValue;

end;

procedure TTest.DoSomething;
begin
   Writeln(SomeValue);
end;
Die Verwendung ist absolut easy. :)
Delphi-Quellcode:
procedure TTestUsage.DoSomething;
var
   List                : TTestList;
   Item                : TTest;
   Counter             : Integer;
begin

   List := TTestList.Create();
   try

      for Counter := 1 to 100 do
      begin
         Item := TTest.Create(Counter);
         List.Add(Item);
      end;

      for Counter := 0 to List.Count do
      begin
         List[Counter].DoSomething(); // ohne type cast :-)
      end;

   finally
      List.Free();
   end;
end;
Ohne ^, @, Typecasts, anderes Gefriemel... :-) (Die Typecasts sind ja schon im Template versteckt :zwinker: )


Alle Zeitangaben in WEZ +1. Es ist jetzt 17:15 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz