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/)
-   -   [XE3] Operator Overloading, Verhalten? (https://www.delphipraxis.net/179629-%5Bxe3%5D-operator-overloading-verhalten.html)

4dk2 20. Mär 2014 14:57

Delphi-Version: XE3

[XE3] Operator Overloading, Verhalten?
 
Moin,

Vorhin zum ersten mal mit Delphi und Operator Overloading beschäftigt.
Von C++ kenn ich es schon.
Das Delphi das seit Delphi2006 schon kann war mir neu.

Bin vorhin erstmal auf die Nase gefallen.
Ich dachte mir schön, dann leg ich jetzt mal nen eigenen Typ an
und hinterlege da einen Wert + Verknüpfung auf ein Objekt.

Hab dann die Variable lokal angelegt, und einfach mit
v.object := irgendwas;
und
v:=100;
initialisiert.

Das klappt auch.
ABER! wenn v ein feld einer klasse ist oder im globalen Teil steht,
wird bei v:=100 das record neu initialisiert. Also der Pointer auf das Objekt ist dann nil.
Interessanter weise bei lokalen variablen nicht. Auch nicht bei Release...

Gut man kann das verhalten schnell korrigieren indem man alle Felder vom Record im class operator implict
initialisiert...

Aber ich hätte das Verhalten gerne.

Also ich möchte am liebsten dem Record beim Anfang ein Objekt oder zweiten wert mitgeben, den
es bei einer Operator Zuweisung behält (v:=100)
Ist denke ich aber nicht möglich oder?

Hier das Beispiel:

Delphi-Quellcode:
unit Unit2;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TVarX = record
    Value: integer;
    FormMaster: TForm;
    Name: string;
    class operator implicit(const z: integer): TVarX; // bei TvarX:=int
  end;

  TForm2 = class(TForm)
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
  private
    FormVar1: TVarX;
    { Private-Deklarationen }
    procedure TestX;
  public
    { Public-Deklarationen }
  end;

var
  Form2: TForm2;
  globalVar1: TVarX;

implementation

{$R *.dfm}
{ TVarX }

class operator TVarX.implicit(const z: integer): TVarX;
begin
  result.Value := z;
end;

{ TForm2 }

procedure TForm2.TestX;
var
  localVar1: TVarX;
begin
  localVar1 := 100;
  localVar1.Name := 'blubblub';
  localVar1.FormMaster := self;

  localVar1 := 200; { value = 200, name = 'blubblub', formMaster=form2 }

  FormVar1 := 100;
  FormVar1.Name := 'blubblub';
  FormVar1.FormMaster := self;

  FormVar1 := 200; { value = 200, name = '', formMaster=nil }

  globalVar1 := 100;
  globalVar1.Name := 'blubblub';
  globalVar1.FormMaster := self;

  globalVar1 := 200; { value = 200, name = '', formMaster=nil }



  //zum testn bei build release...
  Memo1.Lines.Add('localVar1.Name' + ' : ' + localVar1.Name); //out blubblub
  Memo1.Lines.Add('FormVar1.Name' + ' : ' + FormVar1.Name);  //out = ''
  Memo1.Lines.Add('globalVar1.Name' + ' : ' + globalVar1.Name); //out = ''

end;

procedure TForm2.FormCreate(Sender: TObject);
begin
  TestX;
end;

end.

Uwe Raabe 20. Mär 2014 15:23

AW: [XE3] Operator Overloading, Verhalten?
 
Ich denke eher das Verhalten bei localVar1 ist der Fehler. Der Implicit-Operator gibt immer einen kompletten Record zurück. Man hat eigentlich keinen Zugriff auf das Ziel der Zuweisung. Das wird auch deutlich, wenn du mal den Add-Operator implementierst und dann einfach mehrere Additionen in eine Zuweisung schreibst. Die daraus resultierenden Zwischenergebnisse müssen ja irgendwo liegen.

4dk2 20. Mär 2014 15:43

AW: [XE3] Operator Overloading, Verhalten?
 
Habs direkt mal getestet:

Delphi-Quellcode:

class operator TVarX.add(const a, b: TVarX): TVarX;
begin
  result.Value:= a.Value+b.Value;
end;
.....

  localVar1,l2,l3,lg: TVarX;
......
  localVar1:=100;
  lg.Name:='asdfg';
  l2:=localVar1;
  l3:=400;
  lg:=l2+l3; { value = 500, name = 'asdfg' }
Also wird im lokalen Teil, nur der veränderte Wert übernommen.
"lg" hat ja immer noch den zugewiesenen Wert vor der Addition.

jaenicke 20. Mär 2014 15:47

AW: [XE3] Operator Overloading, Verhalten?
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1252774)
Ich denke eher das Verhalten bei localVar1 ist der Fehler.

Richtig, der Compiler "vergisst" an der Stelle den Aufruf von CopyRecord usw., so dass die Werte nicht überschrieben werden.
Bei den beiden anderen Varianten wird der Record nach dem Operator jeweils überschrieben.

Ich glaube auch nicht, dass das Absicht ist.

himitsu 20. Mär 2014 16:19

AW: [XE3] Operator Overloading, Verhalten?
 
Delphi-Quellcode:
class operator TVarX.implicit(const z: integer): TVarX;
begin
  Result.FormMaster := nil;
  Result.Value := z;
end;
Ohne FormMaster:=nil kann es sein, daß FormMaster einen undefinierten Zufallswert enthält. (nicht NIL)
Und nun fällt es auch auf, warum dein .object zwangsweise verschwinden muß.

Zitat:

Hab dann die Variable lokal angelegt, und einfach mit
v.object := irgendwas;
und
v:=100;
initialisiert.
Das ist auch richtig so, denn du weißt ja dem "kompletten" Record einen neuen Wert zu und alles was vorher drin stand, ist somit hinfällig.

Im Prinzip entspricht dein Code ja Folgendem, egal ob es jetzt ein Objekt oder Record ist. (wobei eher Interface, statt Objekt, wegen der automatischen Speicherverwaltung)
Delphi-Quellcode:
v.FormMaster := irgendwas;
v := TVarX.Create(100); // Parameter für "Value"

// bzw.

v.FormMaster := irgendwas;
v := TVarX.Create;
v.Value := 100;

himitsu 20. Mär 2014 16:26

AW: [XE3] Operator Overloading, Verhalten?
 
Zitat:

Zitat von jaenicke (Beitrag 1252779)
Zitat:

Zitat von Uwe Raabe (Beitrag 1252774)
Ich denke eher das Verhalten bei localVar1 ist der Fehler.

Richtig, der Compiler "vergisst" an der Stelle den Aufruf von CopyRecord usw., so dass die Werte nicht überschrieben werden.
Bei den beiden anderen Varianten wird der Record nach dem Operator jeweils überschrieben.

Ich glaube auch nicht, dass das Absicht ist.

Rate mal, warum ich ständig predige, daß man ein Result "immer" vollständig initialisieren muß.

Delphi-Quellcode:
function GetStr(Value: integer): string;
begin
  Result := Result + IntToStr(i);
end;

var
  i: Integer;
  S: string;
begin
  S := '';
  for i := 0 to 100 do
    S := S + GetStr(i);
  ShowMessage(S);
end;
Denn bei Records (über 8 bytes oder mit gemanageten Typen) und gemanageten Typen (Strings, Interfaces usw.) baut der Compiler das intern so um.
Delphi-Quellcode:
procedure GetStr(Value: integer; var Result: string);
begin
  Result := Result + IntToStr(i);
end;

var
  i: Integer;
  S, AutoTempVar: string;
begin
  S := '';
  for i := 0 to 100 do begin
    GetStr(i, AutoTempVar);
    S := S + AutoTempVar;
  end;
  ShowMessage(S);
end;

JamesTKirk 21. Mär 2014 06:34

AW: [XE3] Operator Overloading, Verhalten?
 
Zitat:

Zitat von 4dk2 (Beitrag 1252764)
Das Delphi das seit Delphi2006 schon kann war mir neu.

Und FPC kann das schon viel länger :P

Zitat:

Zitat von 4dk2 (Beitrag 1252764)
Delphi-Quellcode:
class operator TVarX.implicit(const z: integer): TVarX;
begin
  result.Value := z;
end;

Das hier ist falsch. Ein Operator in Delphi ist implizit immer eine statische Funktion. Das heißt es gibt kein
Delphi-Quellcode:
Self
und du kannst damit auch nicht davon ausgehen, dass Felder irgendwie übernommen werden. Hier schlägt dann nämlich die Freiheit des Compilers zu, wie er das Result bzw. die Variable, auf die das Result von Implicit() zugewiesen wird, an die Implicit-Funktion übergeben wird (entweder ein Pointer auf eine bisherige Variable, Pointer auf eine temporäre Variable, die dann zu der anderen zugewiesen wird, etc.). Wie himitsu also bereits gesagt hat, musst du Result immer vollends initialisieren.

Ich hab deinen Code auch mal unter FPC ausprobiert (wo das selbe Ergebnis rauskommt) und wenn man sich den generierten Assemblercode anschaut, so sieht man, dass im Fall von localVar1 die Adresse dieser Variablen selbst an Implicit() übergeben wird, während in den beiden anderen Fällen eine lokale temporäre Variable verwendet und diese anschließend an globalVar1 und FormVar1 kopiert wird.

Gruß,
Sven

4dk2 21. Mär 2014 07:25

AW: [XE3] Operator Overloading, Verhalten?
 
OK, also ich denke wir können das damit abschließen,
ABER seht ihr da irgendeine alternative Möglichkeit mit, (nennen wir es mal Objekte) mit Objekten rechnen Operationen durchzuführen, und im Hintergrund
jedem Objekt eine Referenzen auf andere Objekte zu halten?
Ohne z.b. variable.wert!

Bisher hab ich das so ähnlich gemacht:
Delphi-Quellcode:
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, System.Generics.Collections;

type
  TVarIntX = record
    ValuePtr: PInteger;
    Objekt: TObject;
  end;

  TVarInt_List = TList<TVarIntX>;

  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    TestInt: integer;
    ListTest: TVarInt_List;
    procedure Test;
    function FindObjekt(varptr: PInteger): TObject;
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function TForm1.FindObjekt(varptr: PInteger): TObject;
var i:integer;
    v:TVarIntX;
begin
  result:=nil;
  for I := 0 to ListTest.Count-1 do
  begin
    v:=ListTest[i];
    if v.ValuePtr=varptr then
    begin
      result:=v.Objekt;
      break;
    end;
  end;
end;

procedure TForm1.Test;
var
  v: TVarIntX;
  o:Tobject;
begin
  // initialisieren..
  v.ValuePtr := addr(TestInt);
  v.Objekt := self;
  ListTest.Add(v);
  // ------------------

  TestInt := 100;
  TestInt := TestInt*2;
  TestInt := TestInt+200;

  //jetzt zugeordnetes objekt holen für irgendwas...
  o:=FindObjekt(addr(TestInt));
  if Assigned(o) and (o is TForm) then
    (o as TForm).Caption:='test';


end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ListTest := TList<TVarIntX>.create();
  Test();
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  ListTest.Free;
end;

end.

Uwe Raabe 21. Mär 2014 07:31

AW: [XE3] Operator Overloading, Verhalten?
 
Entweder ist dein Beispiel schlecht konstruiert oder du siehst den Wald vor lauter Bäumen nicht.

Warum der Umweg über den ValuePtr? Sag doch einfach
Delphi-Quellcode:
type
  TVarIntX = record
    Value: Integer;
    Objekt: TObject;
  end;
und in den Berechnungen nimmst du gleich
Delphi-Quellcode:
v.Value
anstatt
Delphi-Quellcode:
TestInt
.

4dk2 21. Mär 2014 07:50

AW: [XE3] Operator Overloading, Verhalten?
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1252833)
Entweder ist dein Beispiel schlecht konstruiert oder du siehst den Wald vor lauter Bäumen nicht.

Ist jetzt nicht das optimale Beispiel :)


Zitat:

und in den Berechnungen nimmst du gleich
Delphi-Quellcode:
v.Value
...
Das will ich ja vermeiden.

In meinem Fall geht es um Mathematische Berechnungen mit Hunderten von diversen Variablen, die unterschiedlichen Objekten zugeordnet sind (teilweise auch Datenbanken).
bei allen Variablen ein .value anzuhängen macht da keinen Sinn.

Bisher gibt es vorm berechnen ein Load() und nach dem berechnen ein Save(), die jeweils ein paar seiten umfassen.
Ich dachte mir, dass man das Load und Save evtl. vereinfachen könnte.
Klar einmal Initialisieren bleibt, aber Load und Save könnten dann zwei schleifen sein.

Delphi-Quellcode:
for i := 0 to ListX.count-1 do
  ListX[i].Save() //Load()


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