Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Records, Pointer, Listen & Co. (https://www.delphipraxis.net/77615-records-pointer-listen-co.html)

Evian 21. Sep 2006 22:16


Records, Pointer, Listen & Co.
 
Hallo Zusammen,

da ich schon seit ein paar Jahren nicht mehr mit sowas gearbeitet habe, wollt ich einfach noch mal hier nachfragen. Also ich möchte in meinem Projekt ein unbestimmte Anzahl an Informatismengen dynamisch speichern. Jede dieser Informationsmengen besteht aus einem tPoint und vier boolchen Werten, weshalb sich hier ja ein Record anbieten würde. Die einzelnen Mengen sind auch liniear mit einander verbunden, weshalb ich sie per pointer zu einer einseitigen Liste verketten könnte. Was ich nun nicht mehr ganz weiß ist: Muss ich ich, wenn ich die Informationsmengen "verwerfen" möchte, mich auch konkret selber um die "Entsorgung" kümmern, indem ich sie z.B. auf NIL setze, oder passiert das automatisch im Hintergrund?! Oder gibt es vielleicht noch einen wesentlich unkomplizierteren Weg (z.B. mit Hilfe von tList) um mein Vorhaben zu realisieren?

würde mich über ein Paar Tips sehr freuen,

grüße, Evian

tn249 22. Sep 2006 00:57

Re: Records, Pointer, Listen & Co.
 
Sers,

ein Weg der vielleicht etwas umständlich ist, aber ganz gut geht ist folgender:

du definierst das was du als record definieren woltest als klasse ( zb. TMyData ) und definierst den TPoint und die 4 bools als felder der klasse.

jetzt kannst du eine TObjectList anlegen. dann erzeugst du deine TMyData klassen und fügst sie der ObjectList hinzu.

der trick dabei ist einen Wert der TobjectList, der sich OwnsObject oder so ähnlich nennt , auf true zu setzen, weil dann die objectlist beim löschen einzelner items oder vernichten der gesamten liste die vorher hinzugefügten object freigibt.


wie gesagt. evtl. ist das ein overkill aber es funktioniert.

Gruß
tn249

bttb930 22. Sep 2006 01:03

Re: Records, Pointer, Listen & Co.
 
Zitat:

Zitat von tn249
wie gesagt. evtl. ist das ein overkill aber es funktioniert.

Absolut falsch: Das ist kein Overkill sondern der richtige Weg: Genau so wie Du es beschrieben hast macht man das.

Und zum ersten Beitrag: Setzt Du Pointer auf nil, dann hast Du noch nicht den Speicherbereich auf den die Pointer vorher gezeigt haben frei gegeben. Aber arbeite einfach nicht explizit mit Pointern - Delphi macht das ja implizit sobald Du eine Klasse definierst. Frei geben kannst Du Klasseninstanzen indem Du deren Free-Methode aufrufst. Dabei musst Du aufpassen, dass Du nichts doppelt frei gibst. Hast Du beispielsweise das von tn249 erwähnte OwnsObjects-Feld auf True gesetzt, dann gibt die Liste alle Elemente frei, sobald sie selbst frei gegeben wird. D.h. du darfst diese nicht vorher selbst frei geben.

oki 22. Sep 2006 07:27

Re: Records, Pointer, Listen & Co.
 
Hi Leute,

das mit dem nil funzt nur, wenn man ein dynamisches Array anlegt. Hier kann man sicher folgendes machen:
Delphi-Quellcode:
type
  TMyData = Record
    Point  : TPoint;
    Param1  : Boolean;
    Param2  : Boolean;
    Param3  : Boolean;
    Param4  : Boolean;
  end;

type
  TDataList = array of TMyData;


type
  TForm1 = class(TForm)
  private
    { Private-Deklarationen }
    FDataList : TDataList;
  public
    { Public-Deklarationen }
    procedure AddData(Data : TMyData);
    procedure Clear;
  end;


var
  Form1: TForm1;

implementation

{$R *.DFM}

{ TForm1 }

procedure TForm1.AddData(Data: TMyData);
begin
  IF High(FDataList) = 0 then SetLength(FDataList, 1) else
    SetLength(FDataList, Length(FDataList) + 1);
  FDataList[High(FDataList)] := Data;
end;

procedure TForm1.Clear;
begin
  FDataList := nil;
end;
Das sieht sicher recht einfach aus. Ich persönich habe aber fest gestellt, dass man immer wieder zusätzliche Methoden für den Zugriff auf die einzelnen Elemente in der Liste benötigt.

Dann kommt man von selbst auf das was bttb930 sagt. Ich benutze übrigens nur noch diese Methode.
Der Vorteil ist unter anderem, dass du häufig benötigte Functionen in dein Listenobject integrieren kannst und nebenbei natürlich schon ne Menge Funktionalität vor findest.

Ein kleines Bsp. hier:
Delphi-Quellcode:
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  contnrs;

type
  TMyData = class(TObject)
  private
    FParam1: Boolean;
    FParam4: Boolean;
    FParam2: Boolean;
    FParam3: Boolean;
    FPoint: TPoint;
  protected
  public
    Constructor Create(APoint : TPoint;
                       Param1, Param2, Param3, Param4 : Boolean); overload;
    property Point  : TPoint read FPoint write FPoint;
    property Param1  : Boolean read FParam1 write FParam1;
    property Param2  : Boolean read FParam2 write FParam2;
    property Param3  : Boolean read FParam3 write FParam3;
    property Param4  : Boolean read FParam4 write FParam4;
  end;

type
  TDataList = class(TObjectList)
  private
  protected
  public
    Function GetPointfromIndex(Index : Integer):TPoint;
  end;

type
  TForm1 = class(TForm)
  private
    { Private-Deklarationen }
    FDataList : TDataList;
  public
    { Public-Deklarationen }
    procedure AddData(Data : TMyData);
  end;


var
  Form1: TForm1;

implementation

{$R *.DFM}

{ TForm1 }

procedure TForm1.AddData(Data: TMyData);
begin
  IF not Assigned(FDataList) then Exit;
  FdataList.Add(Data);
end;

{ TMyData }

constructor TMyData.Create(APoint: TPoint; Param1, Param2, Param3,
  Param4: Boolean);
begin
  inherited create;
  FParam1 := Param1;
  FParam2 := Param2;
  FParam3 := Param3;
  FParam4 := Param4;
  FPoint := APoint;
end;

{ TDataList }

function TDataList.GetPointfromIndex(Index: Integer): TPoint;
var Data : TMyData;
begin
  // vorbelegen
  Result := Point(-1, -1);
  // bei blödsinnigem Index raus
  IF (Index < 0) or (Index >= self.Count) then Exit;
  // Typ-casting
  Data := TMyData(self.Items[Index]);
  try
    IF not assigned(Data) then Exit;
    Result := Data.Point;
  except
    Result := Point(-1, -1);
  end;
end;
o.k. ich hab mir hier jetzt das creieren und frei geben der Liste gespart, aber es soll nur mal die grundlegende Vorgehensweise zeigen. Wie in den vorherigen Beiträgen beschrieben wird jetzt mit der Freigabe der Liste automatisch auch jedes enthaltene Data-Objekt mit frei gegeben.

das wars von mir,

Gruß oki

bttb930 22. Sep 2006 09:05

Re: Records, Pointer, Listen & Co.
 
Ich mache es ein klein wenig anders als oki:

bei mir ist TDataList NICHT von TObjectList abgeleitet sondern ANTHÄLT eine TObjectList:

Delphi-Quellcode:
type
  TDataList = class
  private
    FList: TObjectList;

    function GetCount: Integer; // gibt FList.Count zurück
    function GetItem(Index: Integer): TMyData; // gibt FList[i] as TMyData zurück
    ...
  public
    constructor Create; // erstellt die Liste mit FList := TObjectList.Create(True{AOwnsObjects});
    destructor Destroy; // löscht die Liste mit FList.Free;
    procedure Clear;   // ruft FList.Clear auf

    property Count: Integer read GetCount;
    property Items[Index: Integer]: TMyData read GetItem;
    ...
  end;
Nachteil: Ich muss alle Funktionen die ich aus der ObjectList brauche nochmal schreiben (Clear usw), die Inhalte sind aber kurz da sie ja nur die entsprechenden FList-Funktionen aufrufen (FList.Clear usw).

Vorteil: Der Benutzer dieser Klasse (das kann ja ein ganz anderer Programmierer sein) kann in diese Liste nur die von mir gewünschten Objekte einfügen, also beispielsweise nur TMyData. Andere Klassen werden - anders als beim Ableiten von TObjectList - nicht angenommen. Dafür entfällt dann der Typecast (bei oki die Zeile Data := TMyData(self.Items[Index]); die besser Data := Items[Index] as TMyData wäre aber nun Data := Items[Index] sein muss).

Wie oki das macht hab ich's früher auch gemacht, aber die Erfahrung lehrt mich, dass das zu fehlerträchtig und zuwenig flexibel ist. Ich will auch gar nicht alle public-Funktionen von TObjectList anbieten, sondern nur einen Teil, den ich dann teilweise auch verändern möchte.

oki 22. Sep 2006 10:56

Re: Records, Pointer, Listen & Co.
 
Hi,

grundsätzlich gebe ich bttb930 recht. Bis jetzt war mir das dann aber doch zu viel Aufwand. Dazu muß ich sagen, dass ich in der Regel alleine arbeite und selten jemand meine Classen benutzen muß.

Vielleicht sollte man mal schauen, ob man dann nicht doch die paar methoden von TObjectList überschreibt. Dass könnte dann durchaus so aussehen:
Delphi-Quellcode:
type
  TMyList = class(TObjectList)
  private
  protected
  public
    function Add(AObject: TMyData): Integer;
  end;


{ TMyList }

function TMyList.Add(AObject: TMyData): Integer;
begin
  Result := inherited Add(AObject);
end;
Das ist dann so ala TObjectList aus TList. Alle weiteren Methoden die spezielle Relevanz haben müßten dann natürlich auch überschrieben werden.

Ich will jetzt nicht den Eindruck erwecken unbedingt eine andere Methode als bttb930 nach Vorne zu bringen. Bis heute überschreibe ich auch keine Methoden von TObjectList (warum eigentlich nicht :gruebel: ). dieser Weg viel mir gerade so ein, und ich find ihn gar nicht so schlecht.


Gruß oki

bttb930 22. Sep 2006 11:26

Re: Records, Pointer, Listen & Co.
 
Das Überschreiben ist in diesem Fall aber gefährlich, da die Methode Add NICHT als virtual deklariert ist. Das heißt Du kannst in Deinem Beispiel per Liste.Add(...) zwar nur TMyDatas in die Liste eintragen, per (Liste as TObjectList).Add(...) aber beliebige TObjects. Und das passiert schneller als man denkt, nicht unbedingt durch expliziten TypeCast aber etwa wenn Du eine andere Prozedur hast, die als Parameter eine TObjectList erwartet. Der könntest Du Deine TMyList übergeben (ist ja eine TObjectList), wenn diese Prozedur aber Add aufruft, dann eben nicht TMyList.Add sondern TObjectList.Add.

Evian 22. Sep 2006 11:52

Re: Records, Pointer, Listen & Co.
 
hui, da ist ja doch einiges Zusammen gekommen. Werd nachher erstmal die Vorschlag von Oki umsetzen und sofern es noch irgendwelche Probleme gibt, werd ich mich noch mal melden.

Aber vielen Danke erstmal, für die ausführlichen Beschreibungen.

oki 22. Sep 2006 12:33

Re: Records, Pointer, Listen & Co.
 
Hi,

um es gleich klar zu stellen; bttb hat Recht!

Gut, und nun mein Aber:

Zitat:

Zitat von bttb930
Und das passiert schneller als man denkt, nicht unbedingt durch expliziten TypeCast aber etwa wenn Du eine andere Prozedur hast, die als Parameter eine TObjectList erwartet. Der könntest Du Deine TMyList übergeben (ist ja eine TObjectList), wenn diese Prozedur aber Add aufruft, dann eben nicht TMyList.Add sondern TObjectList.Add.

Wer das macht hat selber Schuld. Mein Object ist eben nicht eine ordinäre TObjectList. Grundsätzlich benenne ich diese Listen immer nach ihrem "Inhalt".

Das muß man sich so vorstellen, dass ich eine Liste der Personen auch TPersonenListe nenne. Ich selber gehe davon aus, dass jemand in einen Behälter nur das gibt, was da rein gehört. Wer in den Kühlschrank die Wäsche legt, hat selber schuld. Da ich aber auch versuche möglichst DAU-sicher zu proggen gebe ich im Grunde bttb recht. In wie Weit mein Vorschlag zum Überschreiben Konventionssicher ist, weis ich auch nicht.

Gruß oki

bttb930 22. Sep 2006 13:25

Re: Records, Pointer, Listen & Co.
 
naja, mit DAUs hat das nicht viel zu tun, aber wenn ich alleine programmiere bin ich auch gerne mal faul.

nur programmiere ich idR im team mit 5-10 anderen entwicklern - und da rächen sich faulheiten früher oder später immer. egal ob DAUs im team sind oder nicht.

grundsätzlich überschreibt man nicht-virtuelle funktionen und prozeduren nicht.

Elvis 22. Sep 2006 13:34

Re: Records, Pointer, Listen & Co.
 
@Oki, was nicht geht, geht nunmal nicht.
Man muss also auch nicht prüfen ob wirklich das drin ist, was drin sein sollte.

Ok, jetzt könnte jemand einwenden, dass Delphi32 nicht wirklich type safe ist, und jemand einen Hardcast seiner Lunchbox auf TSomeType machen könnte.Aber irgendwo muss man verhältnismäßig bleiben, IMHO...

Ich selbst gehe aus genau diesem Grund auch lieber den Weg des Dekorierens. :)

r2c2 22. Sep 2006 14:34

Re: Records, Pointer, Listen & Co.
 
ggf. kann hier das TObjectList TemplateTObjectList Template viel Arbeit ersparen...

mfg

Christian

oki 22. Sep 2006 15:46

Re: Records, Pointer, Listen & Co.
 
Hi,

@Elvis:
Zitat:

Zitat von Elvis
@Oki, was nicht geht, geht nunmal nicht.

Gehn tuts. Das ist nicht die Frage. (oder ich habe hier was falsch verstanden)
Ob das der gute weg mit dem Überschreiben ist, das fragt sich. Dann frage ich auch mal so:

Warum wird in TObjectList die Methode Add aus TList statisch überschrieben?

Also ich war das nicht!!! :angel:

Unabhängig davon ist der Tip mit den Templates beeindruckend. Da hör ich mal auf zu antworten und zieh mir das über's Wochenende mal rein. Dank r2c2!

Gruß oki

Evian 22. Sep 2006 19:21

Re: Records, Pointer, Listen & Co.
 
öhm die frage ist jetzt vielleicht 'n bisschen doof, aber zum Glück darf man hier ja auch "doofe Fragen" stellen. :-D

und zwar wäre meine Frage zu:

Zitat:

Zitat von oki
Ich persönich habe aber fest gestellt, dass man immer wieder zusätzliche Methoden für den Zugriff auf die einzelnen Elemente in der Liste benötigt.

Muss ich jetzt für den Zugriff auf die Werte der TMyData-Objekte immer eine eigene Zugriffsfunktion implementieren, oder kann ich mir nicht auch einfach die einzelnen Objekte komplett aus der Liste holen und dann direkt auf die Werte zugreifen?! Also ich meine sowas wie:

Delphi-Quellcode:

VAR
tempMyData : TMyData;
...

tempMyData := DataList.Items[Index];
x := tempMyData.FParam1
y := tempMyData.FParam2
...

Elvis 22. Sep 2006 19:26

Re: Records, Pointer, Listen & Co.
 
Zitat:

Zitat von oki
@Elvis:
Zitat:

Zitat von Elvis
@Oki, was nicht geht, geht nunmal nicht.

Gehn tuts. Das ist nicht die Frage. (oder ich habe hier was falsch verstanden)

Sorry, war blöd formuliert. Ich meinte, dass man bei einer dekorierten OvjectList schlecht etwas anderes reinwerfen kann als das was in der einzigen Add Methode steht. ;)

oki 23. Sep 2006 11:41

Re: Records, Pointer, Listen & Co.
 
Zitat:

Zitat von Elvis
Ich meinte, dass man bei einer dekorierten OvjectList schlecht etwas anderes reinwerfen kann als das was in der einzigen Add Methode steht. ;)

Ja, und genau das will ich eigentlich auch damit erreichen. Bei mir soll da normaler weise auch nichts anderes rein.


Zitat:

Zitat von Evian
Muss ich jetzt für den Zugriff auf die Werte der TMyData-Objekte immer eine eigene Zugriffsfunktion implementieren, oder kann ich mir nicht auch einfach die einzelnen Objekte komplett aus der Liste holen und dann direkt auf die Werte zugreifen?!

Klar, du mußt hier nicht mit Gewalt Methoden implementieren die du nicht haben willst. Normalerweise ist es jedoch so, dass man in so einer Objekt-Liste Sachen ablegt, auf die man auch öfter mal zugreift.
In deinem letzten Beispiel ist die Einsparung nun gerade nicht erheblich, aber auch da. Mit einer direkten Zuweisung in der Form:
Delphi-Quellcode:
  X := MyDataList.GetXfromIndex(Index);
  Y := MyDataList.GetYfromIndex(Index);
wobei die
Delphi-Quellcode:
Funktion GetXfromIndex(Index : Integer);
alle notwendigen Arbeiten für dich in der klasse übernimmt. die können sein:
- Holen des Data-Objectes bei Index,
- prüfen der Instanz,
- Ermitteln des Wertes für x oder y,
- Gültigkeitsprüfung für x oder y.

Das ist dann schon wieder ne ganze Menge. Hast du diese Zugriffe an mehreren Stellen deines Programms, so hilft das schon ein stück weiter. Zudem hast du es in deinem Object einmal richtig gut durchdacht und dementsprechend geproggt und mußt dir diese Gedanken nicht jedes mal wieder von neuem machen.
das macht man sicher nicht für alle Zugriffe auf die Liste, aber sicher für die gebräuchlichsten.

Gut, wie du es machst bleibt dir überlassen. Wir geben dir hier nur Ideen und unsere Ansichten. Dahinter steht kein Muß. Solange du dich mit einem Weg nicht anfreunden kannst ist es o.k.. Jeder muß auch seinen eigenen Stil finden.

gruß oki

Evian 23. Sep 2006 17:31

Re: Records, Pointer, Listen & Co.
 
Okey vielen Dank noch mal.

Da ich in der Schule ausschließlich strukturierte Programmierung (ok ich nutze ja auch die Klassen und ihre MEthoden von Delphi, aber letztendlich auch eher strukturiert...) gelernt habe fällt es mir immer wieder schwer wirklich objektorientiert zu programmieren. Auf den ersten Blick erscheint mir der Aufwand immer höher bei OOP, aber das dieser erste Blick auch irgendwo trügerisch ist, sollte ich langsam lernen.. :)


Alle Zeitangaben in WEZ +1. Es ist jetzt 01:33 Uhr.

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