AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Dynamisches Array als Klasse

Ein Thema von delphifan2004 · begonnen am 22. Jan 2014 · letzter Beitrag vom 23. Jan 2014
Antwort Antwort
delphifan2004

Registriert seit: 26. Nov 2004
Ort: Dresden
266 Beiträge
 
Delphi 10.3 Rio
 
#1

Dynamisches Array als Klasse

  Alt 22. Jan 2014, 07:24
Hallo,

Hier habe ich eine Dynamic-Array Klasse erstellt, inspiriert durch meine Veröffentlichung vor längerer Zeit hier in der DP unter dem Titel "Dynamische Arrays auch unter Delphi 3" Damals hatte ich für jeden Arraytyp eine separate Klasse erstellt. Heute ist nur noch eine einzige Klasse davon übrig geblieben als eindimensionales Array. Alle anderen Klassentypen habe ich entfernt. Die vorliegende Klasse TGenericDynArray kann Integer-Typen und Pointer speichern. Mit dem Pointer sollte es möglich sein, auch kompliziertere Datenstrukturen in einem solchen Array zu speichern. Leider finde ich den damaligen Thread nicht mehr, weshalb ich nun einen neuen eröffne. Außerdem weiß ich von Lazarus, das dort dynamische Arrays auch als Klasse(n) definiert sind.

Ich hatte zuerst auch an mehrdimensionale Arrays gedacht, daher auch die auf mehrdimensionale Arrays ausgelegte SetLength() Methode. Da jedoch in heutigen Delphi- und Lazarus Versionen dynamische Arrays beliebiger Dimensionalität eh Standard sind, belasse ich es nun bei dem eindimensionalen Array.

Hier nun die Unit:

Delphi-Quellcode:
unit DynArray;

interface

uses
  Sysutils, Classes;

type
  PByte = ^Byte;
  PShortInt = ^ShortInt;
  PSmallInt = ^SmallInt;
  PWord = ^Word;
  PInteger = ^Integer;
  PLongint = ^Longint;

  //Workaround für 1 Dimensionales Array
  TElementsType = (
    etByte, etShortInt, etSmallInt, etWord, etInteger, etLongint, etPointer
  );

  TGenericDynArray = class(TObject)
  private
    FCount: Longint;
    FDims: Longint;
    FSize: Longint;
    //FItem: Longint;
    FType: TElementsType;
    FArray: TList;
    FVector: TStringList;
    function GetItems(Index: Integer): Longint;
    function GetItemP(Index: Integer): Pointer;
    function GetItemType: TElementsType;
    procedure SetItems(Index: Integer; value: Longint);
    procedure SetItemP(Index: Integer; value: Pointer);
  protected
    constructor Create; //Für spätere Verwendung
  public
    //Dieser Constructor instantiiert das Array Objekt
    constructor CreateArray(Elements: Integer; ElType: TElementsType);
    destructor Destroy; override;
    property Count: Longint read FCount; //Anzahl Elemente -> gesamtes Array
    property Dims: Longint read FDims; //Anzahl Dimensionen
    property Size: Longint read FSize; //Größe eines Elementes
    //die Array Elemente
    property Items[Index: Integer]: Longint read GetItems write SetItems; default; //passt dann auf alle Typen
    //Wenn Pointer, dann passt dieser Item besser
    //Aber der LOngint Item wandelt Longint intern in Pointer
    property ItemP[Index: Integer]: Pointer read GetItemP write SetItemP;
    //Hier kann der Datentyp der Elemente gelesen werden
    //Festgelegt wird er im Konstruktor mit dem 2. Parameter
    property ItemType: TElementsType read GetItemType;
    procedure SetLength(ElCount: Longint);
  end;

  T1DimArray = TGenericDynArray; //Fügt sich besser in die Namenskonvention ein



function NewByte(b: Byte): PByte;
function NewShortint(si: ShortInt): PShortInt;
function NewSmallInt(si: SmallInt): PSmallInt;
function NewWord(w: Word): PWord;
function NewInteger(i: Integer): PInteger;
function NewLongint(i: Longint): PLongint;

procedure DisposeShortint(var P: PShortint);
procedure DisposeByte(var P: PByte);
procedure DisposeSmallint(var P: PSmallint);
procedure DisposeWord(var P: PWord);
procedure DisposeInteger(var P: PInteger);
procedure DisposeLongint(var P: PLongint);

procedure InitArray(var A: TList; n: Integer; eltype: TElementsType);
procedure CutArray(var A: TList; n: Integer);

implementation

{ für Dimensionsänderungen während der Laufzeit konzipiert                    }
{ kann natürlich überall benutzt werden, wo man diese Funktionalität braucht  }
{ Das Array selber ist mit TList realisiert und behält deshalb die Daten, die }
 
procedure InitArray(var A: TList; n: Integer; eltype: TElementsType);
begin
  while A.Count < n do
   case eltype of
    etByte : A.Add(NewByte(0));
    etShortint : A.Add(NewShortint(0));
    etSmallint : A.Add(NewSmallint(0));
    etWord : A.Add(NewWord(0));
    etInteger : A.Add(NewInteger(0));
    etLongint : A.Add(NewLongint(0));
    etPointer : A.Add(nil);
   end;
end;
 
{ für Dimensionsänderungen während der Laufzeit konzipiert                    }
{ Das Array selber ist mit TList realisiert und behält deshalb die Daten, die }
{ nicht abgeschnitten werden, automatisch                                     }
 
procedure CutArray(var A: TList; n: Integer);
begin
  while n < A.Count do A.Delete(A.Count-1);
end;

function NewByte(b: Byte): PByte;
var P: PByte;
begin
  GetMem(P, Sizeof(b));
  P^ := b;
  Result := P;
end;

function NewShortint(si: ShortInt): PShortInt;
var P: PShortint;
begin
  GetMem(P, Sizeof(si));
  P^ := si;
  Result := P;
end;

function NewSmallInt(si: SmallInt): PSmallInt;
var P: PSmallint;
begin
  GetMem(P, Sizeof(si));
  P^:= si;
  Result := P;
end;

function NewWord(w: Word): PWord;
var P: PWord;
begin
  GetMem(P, Sizeof(w));
  P^:= w;
  Result := P;
end;

function NewInteger(i: Integer): PInteger;
var P: PInteger;
begin
  GetMem(P , Sizeof(i));
  P^:= i;
  Result := P;
end;

function NewLongint(i: Longint): PLongint;
var P: PLongint;
begin
  GetMem(P , Sizeof(i));
  P^:= i;
  Result := P;
end;


procedure DisposeShortint(var P: PShortint);
begin
  freemem(P, Sizeof(Shortint));
end;

procedure DisposeByte(var P: PByte);
begin
  freemem(P, Sizeof(Byte));
end;

procedure DisposeSmallint(var P: PSmallint);
begin
  freemem(P, Sizeof(SmallInt));
end;

procedure DisposeWord(var P: PWord);
begin
  freemem(P, Sizeof(Word));
end;

procedure DisposeInteger(var P: PInteger);
begin
  freemem(P, Sizeof(Integer));
end;

procedure DisposeLongint(var P: PLongint);
begin
  freemem(P, Sizeof(Longint));
end;


{ TGenericDynArray }

function TGenericDynArray.GetItems(Index: Integer): Longint;
begin
Result:=0;
  case FType of
    etByte : Result := Byte(FArray.Items[Index]^) and $ff;
    etShortInt : Result := Shortint(FArray.Items[Index]^) and $ff;
    etSmallInt : Result := Smallint(FArray.Items[Index]^) and $ffff;
    etWord : Result := Word(FArray.Items[Index]^) and $ffff;
    etInteger : Result := Integer(FArray.Items[Index]^);
    etLongint : Result := LongInt(FArray.Items[Index]^);
    etPointer : Result := Longint(FArray.Items[Index]);
  end;
end;

function TGenericDynArray.GetItemP(Index: Integer): Pointer;
begin
result:=nil;
  if FType = etPointer then Result := FArray.Items[Index];
end;

function TGenericDynArray.GetItemType: TElementsType;
begin
  Result := FType;
end;

procedure TGenericDynArray.SetItems(Index: Integer; value: Longint);
var
  b: Byte;
begin
  case FType of
    etByte: begin b := value and $ff; FArray.Items[Index] := NewByte(b); end;
    etShortInt: begin FArray.Items[Index] := NewShortint(value and $ff); end;
    etSmallInt: begin FArray.Items[Index] := NewSmallInt(value and $ffff); end;
    etWord: begin FArray.Items[Index] := NewWord(value and $ffff); end;
    etInteger: FArray.Items[Index] := NewInteger(value);
    etLongint: FArray.Items[Index] := NewLongint(value);
    etPointer: FArray.Items[Index] := Pointer(value);
  end;
end;

procedure TGenericDynArray.SetItemP(Index: Integer; value: Pointer);
begin
  SetItems(Index, Longint(value));
end;

constructor TGenericDynArray.Create; //für zukünftige Verwendung
begin //nicht zum Instanziieren
  inherited Create;
  FArray := TList.Create;
end;

constructor TGenericDynArray.CreateArray(Elements: Integer; ElType: TElementsType);
var i: Integer; P: Pointer;
begin
p:=nil;
  inherited Create;
  FArray := TList.Create;
  for i := 0 to Elements-1 do
  case ElType of
    etByte : begin FArray.Add(NewByte(0)); FSize := Sizeof(Byte); end;
    etShortInt: begin FArray.Add(NewShortint(0)); FSize := Sizeof(Shortint); end;
    etSmallInt: begin FArray.Add(NewSmallInt(0)); FSize := Sizeof(Smallint); end;
    etWord : begin FArray.Add(NewWord(0)); FSize := Sizeof(Word); end;
    etInteger : begin FArray.Add(NewInteger(0)); FSize := Sizeof(Integer); end;
    etLongint : begin FArray.Add(NewLongint(0)); FSize := Sizeof(Longint); end;
    etPointer : begin FArray.Add(P); FSize := Sizeof(Pointer); end;
  end;
  FCount := FArray.Count;
  FDims := 1;
end;

destructor TGenericDynArray.Destroy;
begin
  FArray.Free;
  inherited Destroy;
end;

procedure TGenericDynArray.SetLength(ElCount: Longint);
var
  delta,ix,switch: Integer; Dims: array[0..1] of Longint;
begin
  Dims[0] := ElCount; //weil vorher Dims: array of Longint, statt ElCount
  if High(Dims)<2 then //Hier wurde die Anzahl aktueller Dimensionen geprüft
  begin //wer mag, kann auf mehrdimensionale Arrays erweitern
    ix := 0; //wie ursprünglich vorgesehen, doch aus technischen
    delta := Dims[0]-FArray.Count; //Gründen nicht weiter verfolgt.
    if delta < 0 then switch := -1 else //Differenz zwichen neuer und alter Anzahl Array-Elemente
    if delta > 0 then switch := +1 else
    switch := 0; //um übersichtlicheren Code zu erhalten
    case switch of
     -1: if Dims[0]>=0 then while ix > delta do { oder while FArray.Count > delta }
         begin //oder CutArray(FArray, Dims[0]);
           FArray.Delete(FArray.Count-1);
           dec(ix);
         end;
      0: ; //Keine Aktion
     +1: while FArray.Count < Dims[0] do //oder InitArray(FArray, Dims[0], FType);
         begin
           case FType of //wenn neue Länge (Elementanzahl) größer als vorherige
            etByte: FArray.Add(NewByte(0));//dann neue Elemente dazu und mit NULL initialisieren
            etShortint: FArray.Add(NewShortint(0));
            etSmallint: FArray.Add(NewSmallint(0));
            etInteger : FArray.Add(NewInteger (0));
            etLongint : FArray.Add(NewLongint (0));
            etPointer : FArray.Add(nil);
           end; { von case Ftype }
         end; { von case +1 -> while do begin }
    end; { von case }
  end; { von if High(Dims) }
FCount := FArray.Count;
end;


end.
Ihr dürft diese Unit per copy & paste in Eure Projekte zu jedem beliebigen Zweck übernehmen.

Soeben hat mir der User "Delphi Laie" den Link zu jenem Thread wieder beschafft. Leider kann ich dort nichts mehr korrigieren. Ich will jedoch den Link zu jenem Thread wenigstens hier setzen, damit die anderen Überlegungen von eventuellen Interessenten auch gefunden werden.

http://www.delphipraxis.net/38721-dy...elphi-3-a.html

Schließlich sind dynamische Arrays unter Lazarus auch als Klassen(n) implementiert.

Geändert von delphifan2004 (22. Jan 2014 um 11:00 Uhr)
  Mit Zitat antworten Zitat
lowmax_5

Registriert seit: 9. Mai 2003
Ort: Münster, NRW
257 Beiträge
 
Delphi 11 Alexandria
 
#2

AW: Dynamisches Array als Klasse

  Alt 22. Jan 2014, 08:00
Hallo,

der Aufwand dafür ist ja recht gross. Wäre es nicht einfacher TObjectList (unit Contnrs) dafür zu verwenden?
  Mit Zitat antworten Zitat
Delphi-Laie

Registriert seit: 25. Nov 2005
1.474 Beiträge
 
Delphi 10.1 Berlin Starter
 
#3

AW: Dynamisches Array als Klasse

  Alt 22. Jan 2014, 09:14
Leider finde ich den damaligen Thread nicht mehr, weshalb ich nun einen neuen eröffne.
Die forumseigene Suchfunktion tut sich tatsächlich etwas schwer damit, aber Tante google ist mal wieder der Retter in der Not: Dynamische Arrays auch unter Delphi 3.
  Mit Zitat antworten Zitat
delphifan2004

Registriert seit: 26. Nov 2004
Ort: Dresden
266 Beiträge
 
Delphi 10.3 Rio
 
#4

AW: Dynamisches Array als Klasse

  Alt 22. Jan 2014, 10:55
Leider finde ich den damaligen Thread nicht mehr, weshalb ich nun einen neuen eröffne.
Die forumseigene Suchfunktion tut sich tatsächlich etwas schwer damit, aber Tante google ist mal wieder der Retter in der Not: Dynamische Arrays auch unter Delphi 3.
Zu spät! Aber egal, habe soeben den Suchbegriff so unter Google eingegeben und bin fündig geworden. Nun bleibt nur noch eine Verlinkung zu ebendiesem Thread, der ja der Ausgangspunkt aller Überlegungen dazu war.
  Mit Zitat antworten Zitat
delphifan2004

Registriert seit: 26. Nov 2004
Ort: Dresden
266 Beiträge
 
Delphi 10.3 Rio
 
#5

AW: Dynamisches Array als Klasse

  Alt 22. Jan 2014, 11:08
Hallo,

der Aufwand dafür ist ja recht gross. Wäre es nicht einfacher TObjectList (unit Contnrs) dafür zu verwenden?
Nein, die gab es unter Delphi 3 noch nicht. Und wenn, wie hätte man das dann mit dieser Objectlist einfacher realisieren können? Das interessiert mich jetzt wirklich.

Die Tlist erhält ja auch keine Objektzeiger, sondern Zeiger auf Variablen des mit ElType vereinbarten Datentyps. Ich hätte höchstens gleich nur Longint oder Pointer zulassen können, da intern der Zeiger in der Liste immer mit 4 Byte Länge gespeichert wird, auch dann, wenn ich nur Byte Werte speichern will. Diese Bytes passen auch in eine Longint Speicherstelle. Ich hätte höchstens statt der TList ein
Array[0..0] of Byte verwenden können, wie in der ersten Version, siehe Link im ersten Beitrag. Dann hätte ich mit Setlength immer die aktuell benötigte Bytezahl festgelegt. Deshalb hatte ich ja in der ersten Version (siehe Link) auch für jeden unterstützten Datentyp eine eigene Klasse.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.132 Beiträge
 
Delphi 12 Athens
 
#6

AW: Dynamisches Array als Klasse

  Alt 22. Jan 2014, 11:15
Wäre es aber nicht etwas "einfacher", das alte Programm von D3 wenigstens auf D7 upzudaten und dann die nativen dynamischen Arrrays zu verwenden,
also jetzt nochmal mit dem Ersatz für diese Arrays rumzuspielen?
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Bjoerk

Registriert seit: 28. Feb 2011
Ort: Mannheim
1.384 Beiträge
 
Delphi 10.4 Sydney
 
#7

AW: Dynamisches Array als Klasse

  Alt 22. Jan 2014, 13:22
Konnte man unter D3 noch nicht einen Integer auf Pointer casten und umgekehrt? Falls ja kannst du die das ganze New/ Disposezeugs schenken?
  Mit Zitat antworten Zitat
Delphi-Laie

Registriert seit: 25. Nov 2005
1.474 Beiträge
 
Delphi 10.1 Berlin Starter
 
#8

AW: Dynamisches Array als Klasse

  Alt 22. Jan 2014, 19:57
Die Unit scheint sich noch weiter vereinfachen zu lassen: Die case-Unterscheidungen zu den verschiedenen Integertypen ließ ich weg und benutze in jedem Falle nur das für die Pointer, also das, was hinter

etPointer:

steht, lasse ich ausführen. Das funktioniert auch, und zwar für alle Integer-Typen.

Abgeleitete Klassen funktionieren auch für Arrays anderen Typs (auch records), sofern der Speicherbedarf konstant ist. Bei Strings funktioniert es bisher leider nur bei solchen mit konstanter Länge (string[x]).

Geändert von Delphi-Laie (22. Jan 2014 um 20:00 Uhr)
  Mit Zitat antworten Zitat
delphifan2004

Registriert seit: 26. Nov 2004
Ort: Dresden
266 Beiträge
 
Delphi 10.3 Rio
 
#9

AW: Dynamisches Array als Klasse

  Alt 23. Jan 2014, 09:28
@himitsu: Wäre es nicht, denn ich der alten Version mußte ich Anpassungen vornehmen, um mehrdimensionale Arrays unterstützen zu können, danach funktionierten zwar die 2- und 3 dimensionalen Arrays, die eindimensionalen aber aus mir unerklärlichen Gründen nicht mehr. Außerdem glaube ich nicht, das per Suchen und Ersetzen einfach die Array Zugriffe geändert werden müssen und gut is, dann ließe sich das tatsächlich updaten. Aber dann mmuss ja auf jeden Fall auch die SetLength Methode in allen Klassen angepasst werden.

@Bjoerk: Das konnte man. Ja. Ok, so ließe sich das TGenericDynArray sicher noch vereinfachen.

@Delphi Laie: Ja das sollte klappen. Meine Intention aber war auch, mit der direkten Zuweisung von Integer Werten arbeiten zu können. Du aber hast Bjoerk's Vorschlag aufgegriffen und nun nur noch die Zuweisung an den Pointer drin. Habe es noch nicht getestet, aber es kann sein, das @Bjoerk Recht hat. Dann könnte ein Longint auch da zugewiesen werden, mit:

Delphi-Quellcode:
var
  myArray: TGenericDynArray;
  I: Longint;

begin
  myArray := TGenericDynArray.CreateArray(5{,ElType würde nicht mehr gebraucht});
  I := 100;
  myArray.ItemsP[2] := Pointer(I);
end.
Habe ich jetzt noch nicht getestet, aber wenn das geht, dann hat Bjoerk Recht.
  Mit Zitat antworten Zitat
Delphi-Laie

Registriert seit: 25. Nov 2005
1.474 Beiträge
 
Delphi 10.1 Berlin Starter
 
#10

AW: Dynamisches Array als Klasse

  Alt 23. Jan 2014, 11:13
Die Aufspaltung in mehrere Integertypen halte ich insofern für entbehlich, als daß die Größenbeschränkung der Integervariablen, wie überhaupt alle Beschränkungen des Computers i.d.R. eher restriktiv und hinderlich sind. Der Pointer agiert wie der größte Integertyp (Integer, der bis 2^31-1 ohne Vorzeichenumschaltung funktioniert) und umfaßt damit gewissermaßen alle Integertypen.

Solang man bei Arrayelementen mit gleichem bzw. konstantem Speicherbedarf bleibt, funktioniert die Unit bestens. Auch ein Record of Integer (benötige ich in meinem Sortierkino) und der Datentyp string[255] funktionieren einwandfrei. Nicht hinbekommen habe ich bisher die Implementierung der dynamischen Datenstruktur "string", aber daran werde ich mich nicht festbeißen.

Erstellt man typabhängig eigene Klassen (dabei kann man sich die Klassenableitung zunutze machen) und man definiert nur die eine typabhängige "property" als "default", entfällt die Adressierung ".Items" der einzelnen Arrayelemente, außerdem entfällt der ^-Operator. Diese Datenstruktur, auch wenn sie auf TList basiert, ist tatsächlich wie ein (dynamisches!) Array benutzbar.

Was Delphi 3 recht ist, ist Delphi 2 billig. Im Anhang ist mein Delphi-2-Projekt, das die Verwendung der verschiedenen Datentypen demonstrieren soll.

Es wundert mich, daß bisher noch keine Mosereinwände á la "Was soll das, Delphi kann das ab Version 4.0 von Hause aus" kamen (Edit: Doch, himitsu äußerte sich). Mich interessierte das, und delphi2004 beschäftigte sich auf meine vorsichtige Anfrage noch einmal damit. Nunmehr funktioniert es für eindimensionale Arrays (mit der genannten Einschränkung) perfekt, und dafür danke ich ihm auch an dieser Stelle noch einmal herzlich! Nicht zuletzt lernt man ja auch bei solchen Experimentalprojekten hinzu.
Angehängte Dateien
Dateityp: zip DynArrays.zip (96,4 KB, 9x aufgerufen)

Geändert von Delphi-Laie (23. Jan 2014 um 20:00 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 11:34 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