Autor Beitrag
drstar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 79
Erhaltene Danke: 2

Windows 8.1/x64
Delphi 10.1
BeitragVerfasst: Mo 25.09.17 14:17 
So, nach langer Zeit bin ich dann auch mal wieder mit einem Problem dabei. Ich schreibe derzeit ein Programm zur Dokumentenverwaltung, und habe dazu eine Klasse entwickelt, die eine Art Datenbank beinhaltet. In dieser Klasse sind auch eine Reihe von Records, zum Teil als Arrays, definiert. Normalerweise haben solche Arrays und Records ja eine eigene Speicherverwaltung, durch die Einbindung in die Klasse scheine ich diese Speicherverwaltung unabsichtlich auszuhebeln. Ganz konkret geht es mir um folgendes:

Unit Database
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
type TDatenbank               = class
  public
  type TKlient         = record
         Status        : integer;
         Index         : integer;
         Vorname       : string;
         Nachname      : string;
         Strasse       : string;
         Ort           : string;
       end;
       PKlient         = ^TKlient;
       TKlientliste    = record
         Count         : integer;
         Liste         : array of TKlient;
       end;
       PKlientliste    = ^TKlientliste;
       constructor create(Path: String);
       function  LastOperation: integer;
       {weitere Deklarationen, die hier keine Rolle spielen}
       private
       procedure LoadDB;
       procedure StoreValue(Klient: PKlient); overload;
       {weitere Deklarationen, die hier keine Rolle spielen}
       var  iKlientenliste      : TKlientliste;
            pKlientenliste      : PKlientliste;
            Error               : integer;
            Vorlagenpfad        : string;
            {weitere Deklarationen, die hier keine Rolle spielen}
end;


constructor TDatenbank.create(Path: String);
begin
  Vorlagenpfad := Path;
  LoadDB;
end;

procedure TDatenbank.LoadDB;
  new(PKlientenliste);
  PKlientenliste := @iKlientenliste;
  iKlientenliste.Count := 0//funktioniert offenbar, jedenfalls kein Fehler!
  {weiterer Code, der hier nicht relevant ist}
end;

procedure TDatenbank.NewKlient(Klient: PKlient);
begin
  StoreValue(Klient);
end;

procedure TDatenbank.StoreValue(Klient: PKlient);
begin
  iKlientenliste.Liste[iKlientenliste.Count+1] := Klient^;
  inc(iKlientenliste.Count);
end;


Unit NewClient
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
procedure TForm2.Button1Click(Sender: TObject);
var Klient  : TDatenbank.TKlient;
    AKlient : TDatenbank.PKlient;

begin

  With Klient do
  begin
    Status   := 1;
    Index    := 1;
    Vorname  := Edit1.Text;
    Nachname := Edit2.Text;
    Strasse  := Edit3.Text;
    Ort      := Edit4.Text;
  end;
  New(AKlient);
  AKlient := @Klient;
  Datenbank.NewKlient(AKlient); // Hier knallt es!
  Dispose(AKlient);
end;


In der Main habe ich die Klasse selbstverständlich instanziiert mit

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
var  Datenbank: TDatenbank;
     Error    : integer;

begin  
  Dtenbank := TDatenbank.create('');
  Error := Datenbank.LastOperation;
  if Error <> 0
  then
    case error of
      1: showmessage('Datenbank ist fehlerhaft, bitte Datenbanküberprüfung durchführen!');
      2: showmessage('Es existiert keine Datenbank, diese muß neu angelegt werden!');
    end;
end;
end;


Mein Problem ist der Zugriff auf Strukturen innerhalb der Datenbank (iKlientenliste) z. B. Dort wirft das Programm entweder Zugriffsverletzungen oder, wenn ich mit ReallocMem arbeite, Fehler bei der Bereichsprüfung. Wie kann ich diese Strukturen richtig initialisieren und mit Daten füttern? Die Zeiger sind ja (hoffentlich) richtig geetzt, ich vermute das Problem bei der Speicherverwaltung und wäre für Hilfe sehr dankbar.
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Mo 25.09.17 15:21 
- Nachträglich durch die Entwickler-Ecke gelöscht -
drstar Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 79
Erhaltene Danke: 2

Windows 8.1/x64
Delphi 10.1
BeitragVerfasst: Mo 25.09.17 15:27 
user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Guten Tag drstar,

ich tippe mal, es liegt an dem dynamischen Array:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
TKlientliste = record
  Count: integer;
  Liste: array of TKlient;  // hier 
end;

Die Problemzeile:

ausblenden Delphi-Quelltext
1:
Datenbank.NewKlient(AKlient); // Hier knallt es!					

ruft StoreValue auf:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TDatenbank.StoreValue(Klient: PKlient);
begin
  iKlientenliste.Liste[iKlientenliste.Count+1] := Klient^;  // Array-Länge noch unbekannt ?!
  inc(iKlientenliste.Count);
end;

Dabei wird nirgends eine Länge für das dynamische Array mit SetLength() festgelegt.


Richtig, die gekapselte Routine StoreValue wird aufgerufen (soll auch so sein), da die Bearbeitung der Datenbank ausschließlich innerhalb der Datenbank geschehen soll, und somit lediglich die gefilterten Datensätze ausgespuckt werden sollen. Da spielt auch eine umfangreiche Fehlerbehandlung der Datenbank eine Rolle, die ich natürlich intern haben will.

Ich müßte also das Array anfangs mit SetLength(0) auf 0 festlegen, um es dann immer, wenn ich einen Wert hinzufüge, um eins zu erhöhen?

Edit:

Hab es jetzt wie folgt bearbeitet:

Unit Database
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
type TDatenbank               = class
  public
  type TKlient         = record
         Status        : integer;
         Index         : integer;
         Vorname       : string;
         Nachname      : string;
         Strasse       : string;
         Ort           : string;
       end;
       PKlient         = ^TKlient;
       TKlientliste    = record
         Count         : integer;
         Liste         : array of TKlient;
       end;
       PKlientliste    = ^TKlientliste;
       constructor create(Path: String);
       function  LastOperation: integer;
       {weitere Deklarationen, die hier keine Rolle spielen}
       private
       procedure LoadDB;
       procedure StoreValue(Klient: PKlient); overload;
       {weitere Deklarationen, die hier keine Rolle spielen}
       var  iKlientenliste      : TKlientliste;
            pKlientenliste      : PKlientliste;
            Error               : integer;
            Vorlagenpfad        : string;
            {weitere Deklarationen, die hier keine Rolle spielen}
end;


constructor TDatenbank.create(Path: String);
begin
  Vorlagenpfad := Path;
  LoadDB;
end;

procedure TDatenbank.LoadDB;
  new(PKlientenliste);
  PKlientenliste := @iKlientenliste;
  iKlientenliste.Count := 0//funktioniert offenbar, jedenfalls kein Fehler!
  setLength(iKlientenliste.Liste, 0);  //neu hinzugefügt
  {weiterer Code, der hier nicht relevant ist}
end;

procedure TDatenbank.NewKlient(Klient: PKlient);
begin
  StoreValue(Klient);
end;

procedure TDatenbank.StoreValue(Klient: PKlient);
begin
  setLength(iKlientenliste.Liste, iKlientenliste.Count+1);  //neu hinzugefügt
  iKlientenliste.Liste[iKlientenliste.Count+1] := Klient^;
  inc(iKlientenliste.Count);
end;


Unit NewClient
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
procedure TForm2.Button1Click(Sender: TObject);
var Klient  : TDatenbank.TKlient;
    AKlient : TDatenbank.PKlient;

begin

  With Klient do
  begin
    Status   := 1;
    Index    := 1;
    Vorname  := Edit1.Text;
    Nachname := Edit2.Text;
    Strasse  := Edit3.Text;
    Ort      := Edit4.Text;
  end;
  New(AKlient);
  AKlient := @Klient;
  Datenbank.NewKlient(AKlient); // Hier knallt es!
  Dispose(AKlient);
end;


In der Main habe ich die Klasse selbstverständlich instanziiert mit

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
var  Datenbank: TDatenbank;
     Error    : integer;

begin  
  Dtenbank := TDatenbank.create('');
  Error := Datenbank.LastOperation;
  if Error <> 0
  then
    case error of
      1: showmessage('Datenbank ist fehlerhaft, bitte Datenbanküberprüfung durchführen!');
      2: showmessage('Es existiert keine Datenbank, diese muß neu angelegt werden!');
    end;
end;
end;


Das Problem bleibt bestehen.
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Mo 25.09.17 15:41 
- Nachträglich durch die Entwickler-Ecke gelöscht -
drstar Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 79
Erhaltene Danke: 2

Windows 8.1/x64
Delphi 10.1
BeitragVerfasst: Mo 25.09.17 15:49 
user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Deine Klientenliste hat doch ein Count-Attribut. Du brauchst einzig danach die Array-Länge auszurichten. Count sollte von Anfang an mit 0 initialisiert werden und mit jeden neuen Klienten erhöht oder beim Löschen, vermindert werden. Im Anschluss setzt du erneut die Array-Länge auf Count und das wars.


Ist doch bereits implementiert - der Fehler bleibt aber (Fehler bei Bereichsprüfung), siehe meine Antwort zuvor, dort habe ich genau das eingefügt.
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Mo 25.09.17 15:57 
- Nachträglich durch die Entwickler-Ecke gelöscht -
OlafSt
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 486
Erhaltene Danke: 99

Win7, Win81, Win10
Tokyo, VS2017
BeitragVerfasst: Mo 25.09.17 16:13 
Eigentlich ist das ganze Design der Klasse... sagen wir gewöhnungsbedürftig.

Heutzutage ist TKlient ebenfalls eine Klasse. Und das Array, das so viele Probleme macht, ist entweder eine TObjectList (bis D2009) oder eine TObjectList<TKlient>.

Sobald man diese ObjectList hat, entfällt das komplette Geraffel mit Speicher holen und freigeben und verwalten. Macht alles die ObjectList.

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
  type TKlient         = class(TObject)
  public
         Status        : integer;
         Index         : integer;
         Vorname       : string;
         Nachname      : string;
         Strasse       : string;
         Ort           : string;
       end;

type TDatenbank               = class
  public
       Klientliste: TObjectList<TKlient>;

       constructor create(Path: String);
       function  LastOperation: integer;
       {weitere Deklarationen, die hier keine Rolle spielen}
       private
       procedure LoadDB;
       procedure StoreValue(Klient: TKlient); overload;
       {weitere Deklarationen, die hier keine Rolle spielen}
            Error               : integer;
            Vorlagenpfad        : string;
            {weitere Deklarationen, die hier keine Rolle spielen}
end;

constructor TDatenbank.Create(Path: string)
begin
   KlientListe:=TObjectList<TKlient>.Create(true);
   ...
end;

_________________
Lies, was da steht. Denk dann drüber nach. Dann erst fragen.
drstar Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 79
Erhaltene Danke: 2

Windows 8.1/x64
Delphi 10.1
BeitragVerfasst: Mo 25.09.17 16:37 
user profile iconOlafSt hat folgendes geschrieben Zum zitierten Posting springen:
Eigentlich ist das ganze Design der Klasse... sagen wir gewöhnungsbedürftig.

Heutzutage ist TKlient ebenfalls eine Klasse. Und das Array, das so viele Probleme macht, ist entweder eine TObjectList (bis D2009) oder eine TObjectList<TKlient>.

Sobald man diese ObjectList hat, entfällt das komplette Geraffel mit Speicher holen und freigeben und verwalten. Macht alles die ObjectList.

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
  type TKlient         = class(TObject)
  public
         Status        : integer;
         Index         : integer;
         Vorname       : string;
         Nachname      : string;
         Strasse       : string;
         Ort           : string;
       end;

type TDatenbank               = class
  public
       Klientliste: TObjectList<TKlient>;

       constructor create(Path: String);
       function  LastOperation: integer;
       {weitere Deklarationen, die hier keine Rolle spielen}
       private
       procedure LoadDB;
       procedure StoreValue(Klient: TKlient); overload;
       {weitere Deklarationen, die hier keine Rolle spielen}
            Error               : integer;
            Vorlagenpfad        : string;
            {weitere Deklarationen, die hier keine Rolle spielen}
end;

constructor TDatenbank.Create(Path: string)
begin
   KlientListe:=TObjectList<TKlient>.Create(true);
   ...
end;


Soweit ich das verstanden habe, ist dann jeder Datensatz ein eigenes Objekt - ich sehe nicht, inwiefern mir das alles einfacher machen soll. Nicht nur, daß ich alles umschreiben müßte, ich müßte mir dann erstmal Gedanken machen, wie ich es anstelle, meine Datensätze als Objekte zu behandeln.
Dennoch verstehe ich nicht, weshalb es zu diesem Fehler kommt. In meiner ersten Antwort (dritter Beitrag dieses Threads) habe ich bei der Initialisierung nicht nur Count auf 0 gesetzt (hatte ich vorher schon), sondern eben auch SetLength auf 0 gesetzt. StoreValue hat ihn dann um 1 erhöht (SetLength := Count+1). Funktioniert auch nicht - irgendwo muß ich also einen Denkfehler haben. Bevor ich jetzt anfange, alles auf TObjectList umzuschreiben, würde ich lieber gern den Fehler ausmerzen - sollte das dann halt nicht gelingen, werde ich mich intensiver mit TObjectList beschäftigen.
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Mo 25.09.17 16:51 
- Nachträglich durch die Entwickler-Ecke gelöscht -
drstar Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 79
Erhaltene Danke: 2

Windows 8.1/x64
Delphi 10.1
BeitragVerfasst: Mo 25.09.17 16:57 
user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Dein Denkfehler war/ist:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
// Dein Edit
procedure TDatenbank.StoreValue(Klient: PKlient);
begin                                                       // Angenommen: Count = 0
  setLength(iKlientenliste.Liste, iKlientenliste.Count+1);  // ArrayLänge = 1 --> .Liste[0] kann initialisiert werden
  iKlientenliste.Liste[iKlientenliste.Count+1] := Klient^;  // .Liste[1] wird Klient zugewiesen, gibts aber nicht!
  inc(iKlientenliste.Count);                                // Count = 0 + 1 = 1
end;


hab gerade den Code testweise wie folgt geändert:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
 
procedure TDatenbank.StoreValue(Klient: PKlient);
begin                                                       // Count ist zu Beginn 0, wird bei Initialisierung von Datenbank auf 0 gesetzt
  setLength(iKlientenliste.Liste, iKlientenliste.Count+1);  // hab ArrayLänge auf 1 gesetzt
  iKlientenliste.Liste[iKlientenliste.Count] := Klient^;    // schreibe in Array.Liste[Count], anfangs also 0
  inc(iKlientenliste.Count);                                // Count auf 1 gesetzt (bzw. später inkrementiert)
end;


Den Code mag das Programm gar nicht - ungültige Zeigeroperation.
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Mo 25.09.17 17:07 
- Nachträglich durch die Entwickler-Ecke gelöscht -
drstar Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 79
Erhaltene Danke: 2

Windows 8.1/x64
Delphi 10.1
BeitragVerfasst: Mo 25.09.17 17:17 
user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
1.) Wo wird:
iKlientenliste.Count := 0; zugwiesen? Würde ich z.B. im Konstruktor der Klasse erwarten.
2.) Hast du mein Beispiel hier übersehen?


Die Zuweisung erfolgt in der Routine TDatenbank.DBLoad, dort zu finden, ich hatte dort auch geschrieben, daß ich problemlos Count auf 0 setzen konnte ohne Fehler.

Die Erhöhung von Count sowie das SetLength von iKlientenliste erfolgt unter StoreValue, da dort auch die Zuweisung und Speicherung des Datensatzes erfolgt. Übersehen habe ich es demnach nicht.
drstar Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 79
Erhaltene Danke: 2

Windows 8.1/x64
Delphi 10.1
BeitragVerfasst: Do 28.09.17 15:52 
So, nun habe ich versucht, es mit TObjectList zu probieren und falle gleich mal auf's Maul.

[dcc32 Fehler] Database.pas(62): E2003 Undeklarierter Bezeichner: 'TObjectList<>'

Fehlt mir eine Unit in der Uses-Klausel oder was muß ich sonst anpassen? Habe Delphi 10.1, falls das relevant ist.

Edit:

Hat sich erledigt, System.Generics.Collections ist die benötigte Unit.
drstar Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 79
Erhaltene Danke: 2

Windows 8.1/x64
Delphi 10.1
BeitragVerfasst: Do 28.09.17 18:33 
So, ich hab jetzt auf TObjectList umgestellt. Funktioniert eigentlich auch alles, nur ein Problem habe ich: TObjectList.count fängt bei 0 an - wie ermittle ich, ob überhaupt schon Objekte alias Datensätze in der Liste drin sind? Count auf 0 prüfen scheidet ja aus, denn 0 ist bereits ein Objekt. Wie stelle ich fest, ob schon Objekte gespeichert sind, bevor ich versuche, auf Objekte in der Liste zuzugreifen, die womöglich nicht existieren?
mandras
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 429
Erhaltene Danke: 107

Win 10
Delphi 6 Prof, Delphi 10.4 Prof
BeitragVerfasst: Do 28.09.17 20:37 
Hallo drstar,

Count kannst/mußt du nehmen.

Wenn die Liste leer ist ist Count 0.

Wenn 3 Einträge in der Liste sind so sind dies die Einträge 0,1 und 2
gerd8888
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 205
Erhaltene Danke: 3

Win7
Delphi 10.1 Starter (kostenlos) Lazarus
BeitragVerfasst: Do 28.09.17 20:50 
Hallo drstar,

Zitat:

Count auf 0 prüfen scheidet ja aus, denn 0 ist bereits ein Objekt. Wie stelle ich fest, ob schon Objekte gespeichert sind

[/quote]

ich würde das count mit NIL prüfen. Das ist dann der Wert bei Count -1 !


Zuletzt bearbeitet von gerd8888 am Do 28.09.17 21:12, insgesamt 1-mal bearbeitet
mandras
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 429
Erhaltene Danke: 107

Win 10
Delphi 6 Prof, Delphi 10.4 Prof
BeitragVerfasst: Do 28.09.17 21:10 
> ich würde das count mit NIL prüfen.

TObjectList.count ist ein Integer - also muß auf 0 geprüft werden
gerd8888
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 205
Erhaltene Danke: 3

Win7
Delphi 10.1 Starter (kostenlos) Lazarus
BeitragVerfasst: Do 28.09.17 21:18 
Hallo mandras,

soweit ich weiss, ist es dann bei dynamischen array (count) -1 entspricht NIL.
Ich bin mir jetzt auch nicht mehr ganz sicher. Normalerweise nimmt man NIL her. Das geht 100 prozentig.
mandras
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 429
Erhaltene Danke: 107

Win 10
Delphi 6 Prof, Delphi 10.4 Prof
BeitragVerfasst: Do 28.09.17 22:32 
drstar schrieb daß nun ein Objekt vom Typ TObjectList verwendet wird.

Hier wird per Count:integer festgehalten wie viele Elemente daran sind
OlafSt
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 486
Erhaltene Danke: 99

Win7, Win81, Win10
Tokyo, VS2017
BeitragVerfasst: Fr 29.09.17 01:25 
gerd888 erzählt Unsinn. Wenn die TObjectList<> nicht initialisiert ist, ist die ganze ObjectList Nil. Ein Zugriff auf das Count-Property gibt dann auch prompt eine Exception.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
var
  TOL: TObjectList<TSomething>;
begin
  if Assigned(TOL) then  //TOL = Nil, wenn noch nicht alloziert
  begin
    if TOL.Count > 0 then //Wenn Liste leer ist Count=0
    begin
      ...
    end;
  end;
end;

_________________
Lies, was da steht. Denk dann drüber nach. Dann erst fragen.