Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Klasse oder Record auf sich selbst? (https://www.delphipraxis.net/130971-klasse-oder-record-auf-sich-selbst.html)

hronny 16. Mär 2009 23:07


Klasse oder Record auf sich selbst?
 
Was ich möchte steht bestimmt schon irgendwo im Forum nur finde ich es nicht. Vielleicht habe ich auch nur einen Denkfehler. Ich möchte ein Record (oder Klasse egal) erzeugen, die wiederum sich als Eigenschaft hat. Ich dachte damit kann ich so eine Art Hierarchie erzeugen, so ähnlich wie es bei XML ist.
Delphi-Quellcode:
  TGroups = class(TObject)
    id: Integer;
    name: String;
    main: Integer;
    fgroup_sorted: Boolean;
    SubGroups: Array of TGroups;
  end;
Oder macht man das ganz anders?

sx2008 16. Mär 2009 23:15

Re: Klasse oder Record auf sich selbst?
 
Zitat:

Zitat von hronny
Ich dachte damit kann ich so eine Art Hierarchie erzeugen...

Du kannst dir das Leben einfach machen und von TComponent ableiten (TGroups = class(TComponent)).
TComponent hat die Properties Components[] und ComponentCount.
Damit kann jedes TComponent-Objekt beliebig viele Unterkomponenten enhalten.

omata 16. Mär 2009 23:25

Re: Klasse oder Record auf sich selbst?
 
Das ist soweit ok, allerdings würde ich mindestens noch einen Destruktor spendieren, der die enthaltenen Objekte freigibt. Und ich würde die Eigenschaften besser schützen, das ist ja gerade der Vorteil einer Klasse gegenüber einem Record.

Reinhard Kern 17. Mär 2009 02:36

Re: Klasse oder Record auf sich selbst?
 
Zitat:

Zitat von hronny
.. Ich möchte ein Record (oder Klasse egal) erzeugen, die wiederum sich als Eigenschaft hat.

Hallo,

kein Problem mit einer Klasse, und gut geeignet für Baumstrukturen. Beispiel:

Delphi-Quellcode:
type

  TCPAObject = class;

  TCPARootObject = class;

  TCPADataObject = class
    ObjCode : TCPACodeStr;
    Root : TCPARootObject;
    Parent : TCPADataObject;
    Next : TCPADataObject;
    KeyWord : TCPAKeyWordStr;
    Depth : integer;
 {$IFDEF DebugWindow} SeqNumber : integer; {$ENDIF}
    constructor create (pRoot : TCPARootObject; pParent : TCPAObject);
    procedure GetNextObject (var CO : TCPADataObject);
    end;

  TCPAObject = class (TCPADataObject)
    SubKeyWord : TCPAKeyWordStr;
    ItemNumber,StringNumber : integer;
    ActDetail : TCPADataObject;
    function NextChar : Char;
    procedure UnGetChar;
    ....
Hier wird nur das Root-Objekt erzeugt und eine XML-artig strukturierte (Text)Datei übergeben, dann erzeugen sich die Objekte selbst (abhängig von ObjCode) bis die Datei abgearbeitet ist.

Gruss Reinhard

hronny 17. Mär 2009 20:14

Re: Klasse oder Record auf sich selbst?
 
Danke, das sind ja eine Menge Antworten in der kurzen Zeit :thumb: . Jetzt muss ich mal hier durchspielen, was am besten ist.

mr_emre_d 17. Mär 2009 22:12

Re: Klasse oder Record auf sich selbst?
 
Ich glaube das was du suchst sind Listen:

Delphi-Quellcode:
PMeineListe = ^TMeineListe;
TMeineListe = record
  Wert1: String;
  Wert2: Integer;
  Selbst: PMeineListe;
end;

quendolineDD 17. Mär 2009 22:16

Re: Klasse oder Record auf sich selbst?
 
Aber ein Zeiger in der Instanz selber auf die eigene Instanz ist doch total sinnlos. Er wird eher Hier im Forum suchenverkettete Listen meinen.

Cyf 17. Mär 2009 22:17

Re: Klasse oder Record auf sich selbst?
 
Vielleicht liegt das jetzt an meinen Geschmack und die OOP-Fetischisten werden mich jetzt steinigen :wink: , aber ist zur Verwaltung von Bäumen nicht eine Klasse, die einen Zeiger auf den Root kapselt und als Aufbau des Baums Records die aufeinander Zeiger enthalten sinnvoller? Bei der Benutzung von außen (außerhalb dieser Klasse) hat man dann trotzdem nur mit einem schönen Interface der Klasse zu kämpfen, während sie innen organisiert sein kann, wie sie will, und innen erspart man sich den nicht unbeträchlichen Overhead, für jedes Element ein Objekt zu erzeugen.

mr_emre_d 17. Mär 2009 22:18

Re: Klasse oder Record auf sich selbst?
 
Lol .. wer sagt dann das die Instanz auf sich "selbst" zeigt ?! Damit meinte ich nur, dass es vom selben Typ ist! Du könntest genauso Next, Previous hinschreiben .. das ist nukke.

MfG

quendolineDD 17. Mär 2009 22:23

Re: Klasse oder Record auf sich selbst?
 
@Cyf:
Das würde aber nicht dem Ansatz von OOP entsprechen ;-)

jfheins 17. Mär 2009 23:44

Re: Klasse oder Record auf sich selbst?
 
Zitat:

Zitat von Cyf
Vielleicht liegt das jetzt an meinen Geschmack und die OOP-Fetischisten werden mich jetzt steinigen :wink: , aber ist zur Verwaltung von Bäumen nicht eine Klasse, die einen Zeiger auf den Root kapselt und als Aufbau des Baums Records die aufeinander Zeiger enthalten sinnvoller? Bei der Benutzung von außen (außerhalb dieser Klasse) hat man dann trotzdem nur mit einem schönen Interface der Klasse zu kämpfen, während sie innen organisiert sein kann, wie sie will, und innen erspart man sich den nicht unbeträchlichen Overhead, für jedes Element ein Objekt zu erzeugen.

Okay, ich steinige dann mal :mrgreen:

Erstens: Ja, kannst du so machen - OOP beinhaltet auch Kapselung ;) Im Grunde kannst du also in der Baum-Klasse machen was du willst, solange ich das Teil problemlos benutzen kann.

Allerdings wird es etwas komplizierter sein, die Baumstruktur mit den records zu schreiben da man ja an alle initialisierungen und konsistenzprüfungen denken muss.

Btw.: Ich würde gerne mal den "nicht unbeträchlichen Overhead für jedes Element ein Objekt zu erzeugen" sehen - wenn ich richtig informiert bin, stehen die Methoden der Klasse sowiso im Speicher herum. Wenn ein Objekt dann angelegt wird, passiert doch eigenlich nichts weiter, als dass ein Speicherbereich reserviert wird, in den dann der Self-Pointer, die Felder und vll. n paar Typinformationen reinkommen. Kommt dann natürlich noch drauf an, was im Konstruktor passiert.

Ist jetzt nicht bös gemeint oder so, aber ich wollte nur mal meinen Senf dazugeben :stupid:
(Nein, es ist nicht der mittelscharfe von Develei ^^)

Cyf 18. Mär 2009 00:45

Re: Klasse oder Record auf sich selbst?
 
Naja beim erzeugen eines Objektes pasiert schon ein wenig mehr, als beim reservieren von Speicher für einen neuen Record auf dem Heap, bei Objekten müssen ja immer auch Zeiger und Informationen auf die (ja, einmalig, statisch angelegten) RTTI angelegt werden zwecks Methodentabelle etc.
Wenn du im Debugger allein mal den Aufruf von TObject.Create verfolgst, dann kommt in etwa sowas:

Code:
TObject.Create:
00403808 84D2             test dl,dl
0040380A 7408             jz $00403814
0040380C 83C4F0           add esp,-$10
0040380F E830030000       call @ClassCreate
00403814 84D2             test dl,dl
00403816 740F            jz $00403827
00403818 E87F030000       call @AfterConstruction
0040381D 648F0500000000   pop dword ptr fs:[$00000000]
00403824 83C40C          add esp,$0c
00403827 C3               ret

@ClassCreate:
00403B44 52               push edx
00403B45 51               push ecx
00403B46 53               push ebx
00403B47 84D2             test dl,dl
00403B49 7C03             jl $00403b4e
00403B4B FF50F4           call dword ptr [eax-$0c] //<- NewInstance
00403B4E 31D2             xor edx,edx
00403B50 8D4C2410         lea ecx,[esp+$10]
00403B54 648B1A          mov ebx,fs:[edx]
00403B57 8919             mov [ecx],ebx
00403B59 896908           mov [ecx+$08],ebp
00403B5C C741046D3B4000   mov [ecx+$04],$00403b6d
00403B63 89410C          mov [ecx+$0c],eax
00403B66 64890A          mov fs:[edx],ecx
00403B69 5B              pop ebx
00403B6A 59               pop ecx
00403B6B 5A              pop edx
00403B6C C3               ret

TObject.NewInstance:
004037D0 53               push ebx
004037D1 8BD8             mov ebx,eax
004037D3 8BC3             mov eax,ebx
004037D5 E826000000       call TObject.InstanceSize
004037DA E8B1F4FFFF      call @GetMem
004037DF 8BD0             mov edx,eax
004037E1 8BC3             mov eax,ebx
004037E3 E85C000000       call TObject.InitInstance
004037E8 5B              pop ebx
004037E9 C3               ret
004037EA 8BC0             mov eax,eax

TObject.InstanceSize:
00403800 83C0D8           add eax,-$28
00403803 8B00             mov eax,[eax]
00403805 C3               ret
00403806 8BC0             mov eax,eax

@GetMem:
00402C90 85C0             test eax,eax
00402C92 7E13             jle $00402ca7
00402C94 FF151C474500     call dword ptr [$0045471c]
00402C9A 85C0             test eax,eax
00402C9C 7402             jz $00402ca0
00402C9E F3C3             rep ret
00402CA0 B001             mov al,$01
00402CA2 E939010000       jmp Error
00402CA7 31C0             xor eax,eax
00402CA9 F3C3             rep ret
00402CAB 90               nop

TObject.InitInstance:
00403844 53               push ebx
//[...]
00403899 C3               ret

@AfterConstruction:
00403B9C 55               push ebp
//[...]
00403BC6 83C408           add esp,$08
00403BC9 EB19             jmp $00403be4
00403BCB E930010000       jmp @HandleAnyException
00403BD0 B201             mov dl,$01
00403BD2 8B45FC          mov eax,[ebp-$04]
00403BD5 E812000000       call @BeforeDestruction
00403BDA E8DD040000       call @RaiseAgain
00403BDF E82C050000       call @DoneExcept
//[...]
00403BE9 C3               ret
Beim Anfordern von Heap-Speicher für einen Record via New(aPointer) wird das ganze hingegen (solange keine Strings o. ä. beteiligt sind, dann kommt noch ein Initialize hinzu) zu nur einem GetMem. Das mag bei kleinen Datenmengen nicht ins Gewicht Fallen, aber lass es mal große Datenmengen sein, dann macht das prozentual schon etwas aus, auch wenn das praktisch nur dann spürbar sein sollte, wenn diese ständig neu erzeugt werden müssen (z.B. irgendwelche Listen über Objekte die ihren Zustand ändern).

Folgender Testcode:

Delphi-Quellcode:
procedure TForm1.FormCreate(Sender: TObject);
var
  Timer: TQPCounter;
  i: Integer;
  p: PInteger;
begin
  Timer:= TQPCounter.Create; //eigene Klasse um die Zeit zu messen, unbedeutent
  for i := 0 to 9999999 do
  begin
    New(p);
    //oder: TObject.Create; über das memory Leak sei mal hinweggesehen, auch das Freigeben dauert beim Objekt länger
  end;
  Showmessage(Timer.ClockStr);
  Timer.Free;
end;
mit Zeiger: 240-330ms
mit Objekt: 670-690 ms

Wie weit das jetzt unter welchen Bedingungen spürbar ist, kann man sich streiten, das ist wahr, bloß der Code zum Verwalten des Baums muss sowieso irgendwo hin, ob jetzt in die Verwaltungsklasse, oder in die Nodes, ist dann egal, also nehm ich die schnellere Variante. :wink:
Natürlich gibt es bestimmt auch Situationen, wo die Klasse Sinn macht, etwa bei Kombinationen von verschiedenen Knotenarten, die irgendwie in Beziehung stehen und anders reagieren sollten.

[Edit]Zeiten mit Dispose bzw. mit Zuweisung des Objekts auf eine Variable und dann MyObj.Free:

leere Schleife: ca. 5,37 ms
Zeiger New und Dispose: ca. 150 ms (ja das ist weniger, liegt nicht an Auslastung im Hintergrund o. ä., habs mehrfach verglichen, es ist immer sehr viel weniger, vermute mal, weil nicht immer kleine Mengen Heap angefordert werden, sondern immer die selbe Stelle, die der Memory-Manager noch hält)
Objekt Create und Free: ca. 815 ms

sx2008 18. Mär 2009 01:30

Re: Klasse oder Record auf sich selbst?
 
Zitat:

Zitat von Cyf
Naja beim erzeugen eines Objektes pasiert schon ein wenig mehr...

Ich kann mir nur ganz wenige Dinge (also Record oder Objekt) vorstellen, bei dem der Overhead der Klasse wirklich ins Gewicht fällt.
Das wären z.B. Koordinaten (x,y) oder komplexe Zahlen (realteil und imaginärteil).
Also Dinge, die einerseits sehr kleine Informationsmengen enthalten und andererseits in hohen Stückzahlen von 100000 oder noch mehr gebraucht werden.
Dann hat ein Record einen spürbaren Performancevorteil gegenüber einer Klassse.
Ein Beispiel wäre ein Partikelsystem, in dem sehr viele kleine Partikel im virtuellen Raum herumfliegen.
Oder Strings - in manchen Progsprachen ist jeder String ein Objekt.
Hier hat Delphi ja seinen eigenen Weg gewählt.

Wenn Beides aber nicht zutrifft (also geringe Datenmenge und sehr hohe Stückzahl), dann ist der Overhead eines Objekt praktisch nicht spürbar.
Dann kann die OOP ihre Trümpfe ausspielen und wer geschickt programmiert brauchst sich um die Leistung keine Sorgen machen.
Manchmal erreicht man mit OOP sogar mehr Leistung also ohne.
Der Grund dafür ist, dass man mit OOP bestimmte Algorithmen verwenden kann (Objektcache, Nullobjekte, Hashing-Verfahren) die man ohne OOP nicht verwenden könnte ohne das der Sourcecode zu einem unwartbaren Klumpen würde.

alzaimar 18. Mär 2009 07:06

Re: Klasse oder Record auf sich selbst?
 
Wenn Performance das einzige Kriterium wäre, dann sollte man von OOP die Finger lassen. In der Tat werden viele der fundamentalen Datenstrukturen ohne OOP implementiert. Das liegt aber in erster Linie daran, das man die Vorzüge der OOP hier nicht braucht. Klar kommt noch der Performancefaktor ins Spiel.
Wenn du also eine Klasse für das Speichern von Bäumen schreiben willst, dann verwende ruhig Records für die Baumstruktur, um beim Einfügen von 1 Mio Knoten einen Geschwindigkeitsvorteil von 400ms zu haben (Nebenbei ist ein Baum keine sonderlich schnelle Datenstruktur).

Die Schnittstelle wird aber eh eine Klasse sein, da hier die OOP-Vorzüge (Stichwort: Erweiterbarkeit) zum Tragen kommen müssen, und sei es, die Methode des Schlüsselvergleiches (ComapareString, CompareText, etc.) verändern zu können.

hronny 29. Mär 2009 21:20

Re: Klasse oder Record auf sich selbst?
 
Ich habe da es nicht 100000 Datensätze sind, jedenfalls mit OOP gelöst und es klappt super.


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:43 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