Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi EInvalidPointer bei verschachtelten dynamischen Arrays (https://www.delphipraxis.net/120660-einvalidpointer-bei-verschachtelten-dynamischen-arrays.html)

alkan 14. Sep 2008 20:52


EInvalidPointer bei verschachtelten dynamischen Arrays
 
Ich verwende in meinem Programm u.a. folgende Objekte:


Delphi-Quellcode:
  TTicket = class
  [...]
 
  TTicketBatch = class
  public
    TicketCount: integer;
    Ticket: array of TTicket;
    procedure AddTicket(InTicket: TTicket);
    procedure Obfuscate(Seed: string; TicketType: byte);
    function GetMasterHash: string;
  end;

  TTicketBatchManager = class
  public
    OwnID: cardinal; // ID of our own node
    BatchCount: integer;
    Batch: array of TTicketBatch;
    constructor Create(InOwnID: cardinal);
    procedure AddTicketBatch(InTicketBatch: TTicketBatch); overload;
  [...]
Die folgende Prozedur läuft zwar noch reibungslos ab:

Delphi-Quellcode:
  procedure TTicketBatchManager.AddTicketBatch(InTicketBatch: TTicketBatch);
  begin
    // Creates a new batch as well as a list entry
    inc(BatchCount);
    SetLength(Batch, BatchCount);
    Batch[BatchCount] := TTicketBatch.Create;
  end;
Wenn in einem zweiten Schritt nun die Grösse des dynamischen Arrays Ticket festgelegt wird...

Delphi-Quellcode:
  procedure TTicketBatch.AddTicket(InTicket: TTicket);
  begin
    inc(TicketCount);
    SetLength(Ticket, TicketCount);  
    Ticket[TicketCount-1] := TTicket.Create;
    Ticket[TicketCount-1] := InTicket;
  end;
führt das zu diversen nicht immer reproduzierbaren Fehlermeldungen ("invalid pointer operation" oder Zugriffsverletzung), seltsamerweise meistens erst nach Beendigung des Programms.

Ich bin nun verunsichert, ob mein Vorhaben auf diese Weise realisierbar ist. Kann man denn überhaupt ein dynamisches Array auf einen Objekt-Datentyp setzen, der seinerseits dynamische Arrays enthält und deren Länge erst zu einem späteren Zeitpunkt bestimmt wird als die Grösse des übergeordneten Arrays?

Wenn ich mir das so durch den Kopf gehen lasse, erscheint mir das ganze als ein Ding der Unmöglichkeit. Denn woher soll der Compiler wissen, wie viel RAM er für das Array Batch reservieren soll, wenn noch nicht festeht, welche Länge die einzelnen Unterelemente vom Typ TTicket ihrerseits haben werden?

Kennt jemand einen Workaround für dieses Problem?

Besten Dank für eure Hilfe!

Roachford 14. Sep 2008 22:00

Re: EInvalidPointer bei verschachtelten dynamischen Arrays
 
Grundsätzlich gehen auch dynamische Arrays zur Objektverwaltung, aber grundsätzlich kann man trotzdem nur nochmal mit Nachdruck auf die Delphi-Referenz durchsuchenTObjectList verweisen.

Zitat:

Zitat von alkan
Denn woher soll der Compiler wissen, wie viel RAM er für das Array Batch reservieren soll, wenn noch nicht festeht, welche Länge die einzelnen Unterelemente vom Typ TTicket ihrerseits haben werden?

Das steht schon fest: 4 Bytes. Jede Instanz wird über ihre Adresse verwaltet, somit hat jeder Array Eintrag bei dir 4 Bytes, also ein Pointer. Mit dem Constructor-Aufruf wird der benötigte Speicherplatz für die Objekt-Instanz reserviert (und initialisiert) und die Adresse des Blockes wird vom Konstruktor zurück gegeben (die Instanz).

Zitat:

Zitat von alkan
Kennt jemand einen Workaround für dieses Problem?

Ja, unbedingt sauber arbeiten und sich nicht selber die Daten überschreiben:

Delphi-Quellcode:
  procedure TTicketBatch.AddTicket(InTicket: TTicket);
  begin
    inc(TicketCount);
    SetLength(Ticket, TicketCount);  
    Ticket[TicketCount-1] := TTicket.Create;
    Ticket[TicketCount-1] := InTicket;
  end;
Du weist dem Eintrag TicketCount-1 eine neue Instanz zu. Danach überschreibst du die Instanz mit der übergebenen Instanz. Damit ist die erste erzeugte Instanz verloren, da alle Referenzen auf diese gelöscht wurden. Und genauso musst du beachten, dass die übergebene Instanz nach der AddTicket() Funktion von der Liste referenziert wird und somit darf der Aufrufer die übergebene Instanz nicht freigeben. Wenn er dies tut, wirst du das später beim Versuch die Instanzen in dem Array freizugeben, bereuen.

Und warum eine Variable TicketCount, wo du doch jederzeit die Länge des Arrays mit Delphi-Referenz durchsuchenLength() ermitteln kannst?

alkan 14. Sep 2008 22:20

Re: EInvalidPointer bei verschachtelten dynamischen Arrays
 
Danke, Roachford, für die schnelle Antwort.

Ich werde Deinen Vorschlag bezüglich TObjectList gerne ausprobieren.

Was meine dynamischen Arrays anbelangt, tritt der Fehler eigentlich schon beim Aufruf von SetLength(Ticket, TicketCount) auf, und nicht erst bei der Zuweisung der Instanzen. Selbst wenn ich diese nämlich ganz ausklammere, produziert meine Anwendung beim Beenden immer noch die Fehlermeldung.


Zitat:

Du weist dem Eintrag TicketCount-1 eine neue Instanz zu. Danach überschreibst du die Instanz mit der übergebenen Instanz. Damit ist die erste erzeugte Instanz verloren, da alle Referenzen auf diese gelöscht wurden. Und genauso musst du beachten, dass die übergebene Instanz nach der AddTicket() Funktion von der Liste referenziert wird und somit darf der Aufrufer die übergebene Instanz nicht freigeben. Wenn er dies tut, wirst du das später beim Versuch die Instanzen in dem Array freizugeben, bereuen.
Das tönt einleuchtend, doch leider liegt die Ursache meines Problems wohl davor.

Hawkeye219 14. Sep 2008 22:36

Re: EInvalidPointer bei verschachtelten dynamischen Arrays
 
Hallo,

vielleicht solltest du zunächst einmal den Fehler in der Methode AddTicketBatch korrigieren:

Delphi-Quellcode:
procedure TTicketBatchManager.AddTicketBatch(InTicketBatch: TTicketBatch);
  begin
    // Creates a new batch as well as a list entry
    inc(BatchCount);
    SetLength(Batch, BatchCount);
//    Batch[BatchCount] := TTicketBatch.Create;
    Batch[BatchCount - 1] := TTicketBatch.Create;
  end;
Gruß Hawkeye

alkan 15. Sep 2008 11:25

Re: EInvalidPointer bei verschachtelten dynamischen Arrays
 
Zitat:

Zitat von Hawkeye219
Hallo,
vielleicht solltest du zunächst einmal den Fehler in der Methode AddTicketBatch korrigieren:

Vielen Dank für den Tipp. Hat natürlich auf Anhieb funktioniert.
(Wie peinlich von mir :wall:)


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