Einzelnen Beitrag anzeigen

Benutzerbild von himitsu
himitsu

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

AW: Delphi vergisst die Werte der Eigenschaften meiner Klasse

  Alt 1. Sep 2011, 00:54
Wie zu erwarten war ... überall Speicherlecks.
Aber erstmal egal


D.Input(@arB,len);
@arB = ein Zeiger auf die Variable
@arB[0] = ein Zeiger auf die Daten
Denn ein dynamisches Array ist intern ein Zeiger und die Daten stecken nicht direkt in der Variable.
Bei einem statischen Array entspicht @arB zwar einem @arB[Low(arB)], aber besser immer mit Index ... nicht daß man es mal vergißt und es dann bei einem Dynamischen knallt.


bis hier ist alles wie es sein soll
falsch gedacht

Delphi-Quellcode:
 D.OutPut(P);
 SetLength(arB,D._RealSize);
 arB := TArByte(P^); //bis hier ist alles wie es sein soll
P gibt einen Speicherbereich (Pointer) zurück
arB ist aber kein billiger Zeiger (Delphi-Referenz durchsuchenPointer), sondern ein array of byte

Dann reservierst du Speicher für das dyn. array (SetLength),
welchen du dann wieder verwirfst (:= ) und versuchst den Pointer als array of byte zu interpretieren,
was natürlich niemals funktionieren kann, da es vollkommen unterschiedliche Typen sind und sie ihren speicher unterschiedlich verwalten.

Du mußt also den speicher von P in das Array kopieren.
Praktisch so ähnlich, wie du schonmal den Text in das Array reinkopiert hast. (Edit1.Text ist der Inhalt von P)
Delphi-Quellcode:
 for I := 0 to len - 1 do
  arB[I] := ord(Edit1.Text[I+1]);
(statt solcher Schleifen kann man natürlich auch Delphi-Referenz durchsuchenMove verwenden)

P köntest du dir dafür nach PStaticBytes casten.
Delphi-Quellcode:
type
  TStaticBytes = array[0..0] of Byte;
  PStaticBytes = ^TStaticBytes;
PS: 0..0 ist ein sonderfall, denn dort macht Delphi niemals eine Bereichsprüfung für den Index.
Man könnte auch [0..666] nehmen (666 = irgendein Wert, welcher groß genug ist ... also mindestens so groß, wie der maximal zu erwartende Indexwert ... da ist [0..0] natürlich einfacher )



Und am Ende vergißt du auch noch P wieder freizugeben, welches in OutPut reserviert wurde.
Darum macht man sowas eigentlich auch besser nicht ... Speicher sollte möglichst immer in der Ebene freigeben werden, wo er reserviert wurde.

Mach Output genauso wie input (Pointer+Len) und laß' die Daten vom Output direkt in @arB[0] reinschreiben.
(Len ist dann die Länge von arB, also die maximale Größe der daten, welche in arb reinpassen würde, was man in Output prüfen sollte, bevor man was reinschreibt)








Delphi-Quellcode:
TBytes = array of Byte; // in neueren Delphis kann man das dann weglassen, da es diesen Typ dort schon gibt

TDataType = (dtNone, dtBool, dtByte, dtChar, dtString, dtInteger, dtReal); // ein Enumerator

TData = class
 private
  _Data : Pointer;
  _MaxSize, _RealSize: Integer;
  _DataType : TDataType;
 public
  procedure Init(MaxSize : Integer; DataType : TDataType); // wäre als Constructor bestimmt besser geeignet
  destructor Destroy; override;

  procedure Input(Data : Pointer; DataSize : Integer);

  property MaxSize: Integer read _MaxSize;
  property RealSize: Integer read _RealSize;
  property DataType: TDataType read _DataType;
  procedure Output(Data : Pointer; MaxSize: Integer);
 end;

implementation

procedure TData.Init(MaxSize, DataType : Integer);
begin
 _MaxSize := MaxSize;
 _DataType := DataType;
 GetMem(_Data, _MaxSize);
end;

destructor TData.Destroy;
begin
 FreeMem(_Data);
end;

procedure TData.Input(Data : Pointer; DataSize : Integer);
begin
 if RealSize <= _MaxSize then
  begin
   _RealSize := DataSize;
   Move(Data, _Data, DataSize);
  end
 else
  ShowMessage('Speicherplatz überschritten!');
end;

procedure TData.Output(Data : Pointer; MaxSize: Integer);
begin
 if MaxSize >= _RealSize then
  Move(_Data, Data, _RealSize)
 else
  ShowMessage('Data zu klein!');
end;
Zitat:
Delphi-Quellcode:
D := TData.Create;
D.Init(500,1);
Mit Init als Constructor, würde dann D := TData.Create(500, 1); gehn.

Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
var
 arB : array of Byte;
 I,Len : Integer;
begin
 len := length(Edit1.Text);
 SetLength(arB, len);
 for I := 0 to len - 1 do
  arB[I] := ord(Edit1.Text[I+1]);
 D.Input(@arB[0], len);
end;

procedure TForm1.Button3Click(Sender: TObject);
var
 arB : TArByte;
 I : Integer;
begin
 SetLength(arB, D.RealSize); // D._RealSize ist PRIVAT ... sowas verwendet man nicht extern
 D.OutPut(@arB[0], Length(arB));
 for I := 0 to High(arB) do
  Caption := Caption + chr(arB[I]);
end;

Enumeratoren sind toll, erstmal ist somit alles logisch und physisch zusammen, was zusammen gehört
und die Quelltextvervollständigung verrät einem was man an soeine Variable/Parameter übergeben kann, da der Compiler nun auch weiß was zusammengehört.
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.

Geändert von himitsu ( 1. Sep 2011 um 01:25 Uhr)
  Mit Zitat antworten Zitat