Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Pointer wirrwarr. Bitte um hilfe (https://www.delphipraxis.net/188162-pointer-wirrwarr-bitte-um-hilfe.html)

hedie 4. Feb 2016 21:32

Pointer wirrwarr. Bitte um hilfe
 
Hallo zusammen

Dieser Code war meine Grundlage:
Delphi-Quellcode:
type
  TTreeData = record
    FCaption: string;
    FColumn1: string;
  end;
  PTreeData = ^TTreeData;
...
procedure TForm1.Button2Click(Sender: TObject);
var
  Node : PVirtualNode;
  Data:PTreeData;
begin

  //Data ist nur ein Pointer auf eine Datenstruktur von TTreeData;
  vst.BeginUpdate;

  //Zuerst holen wir uns den Node
  Node := VST.AddChild(nil);

  //VST kennt bereits die grösse der Struktur und hat hier genau soviel Speicher bereit gestellt
  //Wir übergeben Data nun die Adresse des Speichers von VST.
  Data := vst.GetNodeData(node);

  //Nun können wir direkt in den Node Speicher hinein schreiben.
  Data^.FCaption := 'Test';
  Data^.FColumn1 := 'Column';

  vst.EndUpdate;

end;
Wie man erkennen kann, handelt es sich um VirtualTreeView.


Ich habe nun versucht anstelle des Records ein dynamisches array bestehend aus strings zu benutzen.
Dazu habe ich folgendes versucht:


Delphi-Quellcode:
type
  TsqlData = array of string;
  PsqlData = ^TsqlData;

...


function TeVCLqueryTable.Query(Query: string):integer;
var
  row,col:integer;
  node:PVirtualNode;
  sqlData:PsqlData;
begin
  FMySQL.Query(Query);

  //SQLColCount ist 11
  for col := 0 to FMySQL.SQLColCount - 1 do begin
    FVST.Header.Columns.Add;
  end;

  FVST.BeginUpdate;
 
  SetLength(sqlData^,FMySQL.SQLColCount); //SQLColCount ist 11
  FVST.NodeDataSize := sizeof(sqlData);

  node := FVST.AddChild(nil);
  sqlData := FVST.GetNodeData(node);

  sqlData^[0] := 'Test'; //<-- Hier kommt eine exception

  Result := FMySQL.SQLRowCount;

  FVST.EndUpdate;
end;


Ich wäre froh, wenn mir hier jemand weiterhelfen könnte.

Es geht um das Problem mit den Pointern.
Irgendwas mache ich falsch ^^

Danke

EmWieMichael 5. Feb 2016 07:05

AW: Pointer wirrwarr. Bitte um hilfe
 
Deine Pointer zeigen ins Nirwana. Sieh Dir mal 'New' und 'Dispose' an...

Gruß Michael

Lemmy 5. Feb 2016 07:20

AW: Pointer wirrwarr. Bitte um hilfe
 
Zitat:

Zitat von hedie (Beitrag 1329459)
Ich habe nun versucht anstelle des Records ein dynamisches array bestehend aus strings zu benutzen.
Dazu habe ich folgendes versucht:
.....
Es geht um das Problem mit den Pointern.
Irgendwas mache ich falsch ^^

ich habe zwar schon lange nicht mehr mit dynamischen Arrays gearbeitet, aber wenn ich das noch richtig im Kopf habe, sollte man da über SetLength und co. doch entsprechenden Speicher reservieren? Sprich nach deinem GetNodeData() solltest Du schauen wie groß das Array ist und dann über SetLength ggf. weitere ELemente dazu fügen.

Neutral General 5. Feb 2016 07:42

AW: Pointer wirrwarr. Bitte um hilfe
 
Zitat:

Zitat von Lemmy (Beitrag 1329479)
ich habe zwar schon lange nicht mehr mit dynamischen Arrays gearbeitet, aber wenn ich das noch richtig im Kopf habe, sollte man da über SetLength und co. doch entsprechenden Speicher reservieren? Sprich nach deinem GetNodeData() solltest Du schauen wie groß das Array ist und dann über SetLength ggf. weitere ELemente dazu fügen.

Er hat ein Pointer auf ein dynamisches Array, deswegen muss er wie EmWieMichael gesagt hat mit New das Array erzeugen und danach erst mit SetLength die größe des Arrays setzen.
(Später dann wie gesagt mit Dispose die Arrays frei geben!)

Uwe Raabe 5. Feb 2016 10:58

AW: Pointer wirrwarr. Bitte um hilfe
 
Oder man macht es wie im oberen Beispiel mit den Strings und packt einen Record um das Array. Dann muss man aber auch im OnFreeNode das Array wieder auf nil setzen.

Delphi-Quellcode:
type
  TsqlData = record
    Data: array of string;
  end;
  PsqlData = ^TsqlData;

procedure ...GetNodeDataSize(Sender: TBaseVirtualTree;
  var NodeDataSize: Integer);
begin
  { Alle Nodes haben dieselbe DataSize! Aber nicht alle Nodes müssen zwingend dieselbe Array-Länge haben. }
  NodeDataSize := SizeOf(TsqlData);
end;

procedure ...FreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
var
  sqlData:PsqlData;
begin
  sqlData := Sender.GetNodeData(node);
  sqlData.Data := nil;
end;

function Query(Query: string):integer;
var
  row,col:integer;
  node:PVirtualNode;
  sqlData:PsqlData;
begin
  FMySQL.Query(Query);

  //SQLColCount ist 11
  for col := 0 to FMySQL.SQLColCount - 1 do begin
    FVST.Header.Columns.Add;
  end;

  FVST.BeginUpdate;
  try
    node := FVST.AddChild(nil);
    sqlData := FVST.GetNodeData(node);
    SetLength(sqlData.Data,FMySQL.SQLColCount); //SQLColCount ist 11
    sqlData[0] := 'Test';
  finally
    FVST.EndUpdate;
  end;

  Result := FMySQL.SQLRowCount;
end;

Lemmy 5. Feb 2016 11:19

AW: Pointer wirrwarr. Bitte um hilfe
 
Zitat:

Zitat von Neutral General (Beitrag 1329481)
Er hat ein Pointer auf ein dynamisches Array....

hmm... Ich gebe ja gerne zu, dass ich mit Pointern schon immer so meine Probleme habe. Aber was gibt denn .GetNodeData bitte schön zurück, wenn nicht einen pointer auf einen Speicherbereich in das das array gehört? Den Bereich definiere ich mit NodeDataSize und der VST kümmert sich drum, dass dort ein Pointer mit der Größe angelegt wird. Wie ich den interpretiere ist dann meine Sache - auch wenn ich innerhalb dieses Pointers auf weitere Speicherbereiche zugreife die ich dann zu verwalten habe....

Das hier funktioniert bei mir problemlos:

Delphi-Quellcode:

procedure TForm1.FormCreate(Sender: TObject);
begin
  FVST.NodeDataSize := sizeof(PsqlData);
end;

procedure TForm1.Button1Click(Sender: TObject);
var sqlData:PsqlData;
   node:PVirtualNode;
begin
  node := FVST.AddChild(nil);
  sqlData := FVST.GetNodeData(node);
  SetLength(sqlData^, 2);
  sqlData^[0]:='Hallo';
  sqlData^[1]:='Welt';
end;

procedure TForm1.FVSTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
var sqlData:PsqlData;
idx: Integer;
begin
  sqlData := FVST.GetNodeData(Node);
  CellText := '';
  if Assigned(sqlData) then
  begin
    for idx := 0 to length(sqlData^) - 1 do
    begin
      CellText := CellText + sqlData^[idx];
    end;
  end;

end;
Es fehlt noch die Freigabe der dynamischen Arrays bei beenden.
Ich wüsste auch nicht wo genau jetzt der Unterschied zwischen einem dynamischen Array und dem Record von Uwes Beispielcode sein soll. Es sind am Ende Variablen. Ich wäre echt dankbar, wenn mir jemand das erklären kann...

Uwe Raabe 5. Feb 2016 12:39

AW: Pointer wirrwarr. Bitte um hilfe
 
Zitat:

Zitat von Lemmy (Beitrag 1329507)
Ich wüsste auch nicht wo genau jetzt der Unterschied zwischen einem dynamischen Array und dem Record von Uwes Beispielcode sein soll. Es sind am Ende Variablen. Ich wäre echt dankbar, wenn mir jemand das erklären kann...

Der Unterschied ist rein semantisch. Die Speicherstruktur und der erzeugte Code sind wohl so gut wie identisch. Der Record-Ansatz erfordert halt keine Pointer-Dereferenzierung und ist später leichter zu erweitern, aber das muss hier ja nicht unbedingt ein Kriterium sein.

hedie 5. Feb 2016 17:07

AW: Pointer wirrwarr. Bitte um hilfe
 
Vielen Dank für eure Hilfe.
Dies habe ich nun verstanden und es hat auch geklappt!

Nu habe ich gleich noch eine andere Frage zum selben Thema.

Ich habe bei mir in der hauptunit ein ObjektA erstellt, welches ich im private bereich deklariert habe.
Dieses erstelle ich beim öffnen der Anwendung.

Während der Laufzeit, möchte ich mit diesem ObjektA arbeiten.
Um mit diesem ObjektA arbeiten zu können, habe ich mir eine zweite Klasse geschrieben, welche die entsprechenden "bearbeitungs methoden" beinhaltet. Damit ich mit dieser Klasse arbeiten kann, muss ich davon ja auch ein ObjektB erstellen. Soweit so gut. Klappt auch.

Nun möchte ich bei einem Button click, das ObjektB, welches mir Hilft ObjektA zu bearbeiten, erstellen -> verwenden -> und wieder freigeben.

Hier gibts das Problem. Da ich bisher nicht ans freigeben gedacht habe, hab ich das erst jetzt bemerkt.

Ich übergebe ObjektB folgendes beim Create:

Delphi-Quellcode:
FVST:TVirtualStringTree;
FMySQL:TmySQLe;

constructor TeVCLqueryTable.Create(const VST: TVirtualStringTree; const MySQL:TmySQLe);
begin
  FVST := VST;
  FMySQL := MySQL;
...
Beim ObjectB.Free bekomme ich eine Exception.
Ich vermute mal, dass es dabei auch die übergebenen Objekte VST und MySQL freigegeben hat.
Dann wäre mir klar, dass es nicht mehr funktioniert!

Eigentlich möchte ich nur einen Pointer auf das ObjektA bzw. VST und MySQL damit ich mit diesen bereits existierenden Objekten innerhalb meines neuen Objektes arbeiten kann.

Nach dem Freigeben von ObjektB sollen die Objekte VST und MySQL weiterleben.


Könnte mir hier jemand weiterhelfen?

Vielen Dank :)

hoika 5. Feb 2016 17:10

AW: Pointer wirrwarr. Bitte um hilfe
 
Hallo,
wie sieht denn der Destructor der Klasse TeVCLqueryTable (also von ObjectB) aus, den Constructor sehe ich ja.


Heiko

hedie 5. Feb 2016 17:12

AW: Pointer wirrwarr. Bitte um hilfe
 
Bisher sieht der so aus:

Delphi-Quellcode:
destructor TeVCLqueryTable.Destroy;
begin

  //FVST := nil;
  //FMySQL := nil;

  inherited;
end;
Wie du an den kommentaren siehst, war ich mir nicht ganz sicher... ^^

hoika 5. Feb 2016 17:14

AW: Pointer wirrwarr. Bitte um hilfe
 
Hallo,
das sind dann also Variablen deiner Klasse?


Delphi-Quellcode:
type
  TeVCLqueryTable = class
    private
      FVST:TVirtualStringTree;
      FMySQL:TmySQLe;


constructor TeVCLqueryTable.Create(const VST: TVirtualStringTree; const MySQL:TmySQLe);
begin
  FVST := VST;
  FMySQL := MySQL;
Wenn ja, brauchst du im Destructor gar nichts machen.

Gibst du das Objekt vielleicht aus Versehen 2mal frei?


Heiko

hedie 5. Feb 2016 17:21

AW: Pointer wirrwarr. Bitte um hilfe
 
Ja, das sind variablen.

Nachfolgend das gesamte Interface

Delphi-Quellcode:
type
  TsqlData = record
    Data: array of string;
  end;
  PsqlData = ^TsqlData;

  //Das war ein versuch. Wird nicht benutzt
  PmySQL = ^TmySQLe;
  Pvst = ^TVirtualStringTree;

type
  TeVCLqueryTable = class
    private

      FVST:TVirtualStringTree;
      FMySQL:TmySQLe;
      FsearchMode:boolean;
      FsearchString:string;
      //FsqlData:array of string;

      procedure onGetText(Sender: TBaseVirtualTree;
      Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
      var CellText: string);

      procedure onFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);

      procedure onBeforeCellPaint(Sender: TBaseVirtualTree;
      TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
      CellPaintMode: TVTCellPaintMode; CellRect: TRect; var ContentRect: TRect);

    public

      constructor Create(const VST:TVirtualStringTree; const MySQL:TmySQLe);
      destructor Destroy; override;

      function Query(Query:string):integer;
      procedure FilterNodes(const Text : string);


  end;

implementation
...

Nein, das wird nicht zweimal freigegeben.
Sieht so aus:

Delphi-Quellcode:
procedure TForm6.Button1Click(Sender: TObject);
var
  table:TeVCLqueryTable;
begin

  //VirtualStringTree1 liegt als Objekt zur Designzeit auf dem Form
  //mysql ist eine Variable der Klasse TForm6 und wird zum Programmstart erstellt.


  table := TeVCLqueryTable.create(VirtualStringTree1, mysql);
  table.Query('SELECT * FROM rechnung');
  table.Free;
end;
Mit .free gibts direkt eine exception.
Ohne gibts keine und es funktioniert.

Auch bei mehrmaligem klicken.

hedie 5. Feb 2016 17:29

AW: Pointer wirrwarr. Bitte um hilfe
 
Ich glaube es liegt daran:

Delphi-Quellcode:
constructor TeVCLqueryTable.Create(const VST: TVirtualStringTree; const MySQL:TmySQLe);
begin
  FVST := VST;
  FMySQL := MySQL;

  //Hier weise ich Funktionen und Proceduren zu, welche sich innerhalb des Objekts befinden.
  FVST.OnGetText := onGetText;
  FVST.OnFreeNode := onFreeNode;
  FVST.OnBeforeCellPaint := onBeforeCellPaint;
  FsearchMode := false;


  //Optische einstellungen treffen!
  //FVST.Header.AutoSizeIndex := 0;                            //, hoVisible, hoColumnResize, hoHeaderClickAutoSort
  FVST.Header.Options := FVST.Header.Options + [hoAutoResize];
  FVST.TreeOptions.PaintOptions := [toShowRoot, toThemeAware];
  //FVST.TreeOptions.AutoOptions := [toAutoChangeScale];
  FVST.TreeOptions.MiscOptions := [toFullRepaintOnResize];
end;
Ich weise dem VirtualStringTree ein paar Proceduren und Funktionen zu, welche sich innerhalb des Objekts befinden.
Wie dumm auch ^^ Dann ist es klar, dass es nach dem Free nicht mehr geht.

Nun muss ich mir was überlegen, wie man dies am besten löst, so dass der VirtualStringTree weiterhin seine Daten anzeigt.

hoika 5. Feb 2016 18:33

AW: Pointer wirrwarr. Bitte um hilfe
 
Hallo,
dann darfst du die Klasse nicht freigeben,
oder setzt die Events vor der Freigabe auf nil.

Heiko

hedie 5. Feb 2016 18:45

AW: Pointer wirrwarr. Bitte um hilfe
 
Danke für deine Antwort.

Leider benötigt VirtualStringTree die Events um korrekt zu funktionieren.
Da ich mehrere VSTs auf dem Formular habe, müsste ich für jedes ein eigenes privates Objekt innerhalb meiner Form erstellen. Das erscheint mir sehr unschön.

Ich sollte vielleicht lieber eine Klasse machen, welche von VirtualStringTree ableitet und diese um meine Funktionen ergänzt.

Idealerweise, könnte ich diese dann zur Designzeit auf das Formular ziehen.
Mal sehen, ob ich ein Tutorial finde, welches mir erklärt, wie ich eine Komponente erstllen kann...

By the way, wie kann man ein grosses Projekt übersichtlich halten?
Soll man funktionen zu bestimmten bereichen in eigene units auslagern und diese manuell dort hin kopiere?

hoika 5. Feb 2016 18:56

AW: Pointer wirrwarr. Bitte um hilfe
 
Hallo,
deine einzelnen Darstellungs-Klassen für die einzelnen VSTs nennt man Renderer.
Jeder VST bekomme seine eigene Renderer-Klasse und solange der VST existiert, muss die Renderer-Klasse existieren.
Pro Renderer-Klasse bietet sich eine eigene Datei (Unit) an.

Heiko


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