[Erledigt] Noch eine Frage zu OOP Klassen mit Objektlisten

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

[Erledigt] Noch eine Frage zu OOP Klassen mit Objektlisten

Beitrag von MacWomble »

Ich habe einige Klassen, welche wiederum Objektlisten anderer Klassen beinhalten. Dies funktioniert so weit ganz gut. Jedoch habe ich Probleme beim Speichern in die Datenbank, wenn dies zu verschachtelt wird.

Adresse(Objekt) enthält Kontaktdaten (Objektliste) und Kontakte (Objektliste) das funktioniert so weit.

Kontaktnr:=Adresse.Kontaktdaten.Items[i] //Telefonnummern für Adresse
Kontakt:=Adresse.Kontakte.Items[i] // Ansprechpartner
Kontaktnr:=Kontakt.Kontaktdaten.Items[x] //Telefonnummern für Ansprechpartner

funktioniert alles, nur an der einen Stelle nicht.

Kontakt(Objekt aus Kontakte) enthält Kontaktdaten (Objektliste) das funktioniert in den Fenstern (Anzeige) einwandfrei, jedoch komme ich in der Speichern-Funktion nicht an die Kontaktdaten:

Code: Alles auswählen

 
  TKontakt = class(TObject)
  private   
...
    fKontaktdaten: TKontaktdatenListe;   
  published
...
    property Kontaktdaten: TKontaktdatenListe read fKontaktdaten write fKontaktdaten;
...
 
//Kontakt.Kontaktdaten:= TKontaktdatenliste.Create;  wird an anderer Stelle ausgeführt und es können Daten darin erzeugt und gelesen werden
 
procedure TKontaktListe.WriteListData(IDAdresse: integer);
var
  LastID: integer;
  i: integer;
  cnt: integer;
begin
  debugln('TKontaktListe.WriteListData');
  LastID := 0;
  with dtmBasis.qrySQL do
  begin
    for i := 0 to Self.Count - 1 do                // Link zur Adresse setzen !
    begin                                 
      Self.Items[i].SetIDAdresse(IDAdresse);
    end;
    for i := 0 to Self.Count - 1 do
    begin
      if Self.Items[i].fIsChanged then
      begin
        Self.Items[i].fIsChanged := False;
        if Self.Items[i].fID = 0 then
        begin {Insert}
          DebugLn('  Datensatz wird neu angelegt');
          SQL.Clear;
          SQL.Add('Insert Into Kontakte');
          SQL.Add('(fk_adresse, kon_anrede, kon_titel, kon_vorname, kon_zuname, kon_abteilung, kon_funktion, kon_briefanrede, kon_notiz) ');
          SQL.ADD('VALUES');
          SQL.ADD('(:fk_adresse, :kon_anrede, :kon_titel, :kon_vorname, :kon_zuname, :kon_abteilung, :kon_funktion, :kon_briefanrede, :kon_notiz) ');
          Prepare;
        end
        else
        begin  //Update
          DebugLn('  Datensatz ' + IntToStr(Self.Items[i].fid) + ' wird aktualisiert');
          SQL.Clear;
          SQL.ADD('UPDATE Kontakte SET');
          SQL.ADD('fk_adresse = :fk_adresse,');
 
          SQL.ADD('kon_anrede = :kon_anrede,');
...
          SQL.ADD('WHERE idkontakt = :idkontakt;');
          Params.ParamByName('idkontakt').AsInteger := Self.Items[i].fID;
        end;
 
        Params.ParamByName('fk_adresse').AsInteger := Self.Items[i].fIDAdresse;
...
        Params.ParamByName('kon_notiz').AsString := Self.Items[i].fNotiz;
        try
          ExecSQL;
          DebugLn('    Gespeichert !');
        except
          On E: Exception do
            debugln(' ' + E.Message);
        end;
      end
      else
        DebugLn('  Datensatz ' + IntToStr(Self.Items[i].fID) + ' ist aktuell');
      if LastID = 0 then
        LastID := Self.Items[i].fID;
// Bis hier funktionier es
 
// HIER IST DAS PROBLEM (so funktioniert das allerdings bei den Adressen problemlos):
// fKontaktdaten wird richtigerweise als TKontaktDatenliste erkannt!
// Self.Items[i].fKontaktdaten oder Self.Items[i].Kontaktdaten ist hier aber nicht ansprechbar bzw. führt zu einem 'Hänger' ohne weiter Fehlerangabe
//
       if assigned(Self.Items[i].fKontaktdaten) then // wird noch ausgeführt
           Self.Items[i].fKontaktdaten.WriteListData(Self.Items[i].IDAdresse, LastID); //bleibt hängen, Fehler definitiv NICHT in Kontaktdaten.WriteListData, das wird schon nicht mehr aufgerufen!
    end; //for i
  end;
end;               
 
Zuletzt geändert von MacWomble am Mi 9. Okt 2019, 15:20, insgesamt 5-mal geändert.
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1430
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Noch eine Frage zu OOP Klassen mit Objaktlisten

Beitrag von fliegermichl »

Also zum einen kannst du bei self.irgendwas das self. weglassen.

MacWomble hat geschrieben:// HIER IST DAS PROBLEM (so funktioniert das allerdings bei den Adressen problemlos):
// fKontaktdaten wird richtigerweise als TKontaktDatenliste erkannt!
// Self.Items[i].fKontaktdaten oder Self.Items[i].Kontaktdaten ist hier aber nicht ansprechbar bzw. führt zu einem 'Hänger' ohne weiter Fehlerangabe
//
if assigned(Self.Items[i].fKontaktdaten) then // wird noch ausgeführt
Self.Items[i].fKontaktdaten.WriteListData(Self.Items[i].IDAdresse, LastID); //bleibt hängen, Fehler definitiv NICHT in Kontaktdaten.WriteListData, das wird schon nicht mehr aufgerufen!


Ich vermute, daß du umgekehrt darauf zugreifen musst.
Also statt self.Items[i].fKontaktdaten
Kontaktdaten.Items[i].WriteListData...

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1430
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Noch eine Frage zu OOP Klassen mit Objaktlisten

Beitrag von fliegermichl »

Übrigens kannst Du Items als default property definieren, dann verkürzt sich das auf

Code: Alles auswählen

 
 Kontaktdaten[i].Write...
 
Definiert wird das so:
interface
uses contnrs;
 
type
TKontaktdaten = class ( TObjectList )
 function Get(Index : integer) : TKontakt;
 procedure Put(Index : integer; Item : TKontakt);
 property Items[index : integer] : TKontakt read get write put; default;
end;
 
implementation
 
function TKontaktdaten.Get(Index : integer) : TKontakt;
begin
 Result := TKontakt(inherited Get(Index));
end;
 
procedure TKontaktdaten.Put(Index : integer; Item : TKontakt);
begin
 inherited Put(Index, Kontakt);
end;
 

MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

Re: Noch eine Frage zu OOP Klassen mit Objaktlisten

Beitrag von MacWomble »

fliegermichl hat geschrieben:Also zum einen kannst du bei self.irgendwas das self. weglassen.

MacWomble hat geschrieben:// HIER IST DAS PROBLEM (so funktioniert das allerdings bei den Adressen problemlos):
// fKontaktdaten wird richtigerweise als TKontaktDatenliste erkannt!
// Self.Items[i].fKontaktdaten oder Self.Items[i].Kontaktdaten ist hier aber nicht ansprechbar bzw. führt zu einem 'Hänger' ohne weiter Fehlerangabe
//
if assigned(Self.Items[i].fKontaktdaten) then // wird noch ausgeführt
Self.Items[i].fKontaktdaten.WriteListData(Self.Items[i].IDAdresse, LastID); //bleibt hängen, Fehler definitiv NICHT in Kontaktdaten.WriteListData, das wird schon nicht mehr aufgerufen!


Ich vermute, daß du umgekehrt darauf zugreifen musst.
Also statt self.Items[i].fKontaktdaten
Kontaktdaten.Items[i].WriteListData...


Nein, in WriteListData werden die Items in einer Schleife abgearbeitet.
Rein Gedanklich ist es schon richtig, wie ich das da habe. Bei Adresse.Kontaktdaten geht es so ja auch nur bei Kontakte.Items[i].Kontaktdaten nicht, was ja im Prinzip analog zu Kontakt.Kontaktdaten steht. Aber ich möchte es hier in einer Schleife aller Kontakte abarbeiten.
(Vermutlich auch nicht bei Adressen.Items[]i.Kontaktdaten)
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

Re: [Erledigt] Noch eine Frage zu OOP Klassen mit Objektlist

Beitrag von MacWomble »

:oops: Habs gefunden. Ich habe die Kontaktdaten zu einem falschen Zeitpunkt frei gegeben!

Das mit den Items als Default Property muss ich mir noch genauer ansehen, aber ich glaube das bringt mit keine wesentlichen Vorteile.

Danke jedenfalls für Eure Bemühungen und Tipps!
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1430
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: [Erledigt] Noch eine Frage zu OOP Klassen mit Objektlist

Beitrag von fliegermichl »

MacWomble hat geschrieben:Das mit den Items als Default Property muss ich mir noch genauer ansehen, aber ich glaube das bringt mit keine wesentlichen Vorteile.


Der Vorteil ist, daß du nicht bei jedem Zugriff einen Typecast machen musst. Die Property Items von TObjectList sind vom Typ TObject. Wenn Items eine Liste von speziellen Klasseninstanzen hält, musst du ansonsten bei jedem Zugriff

Code: Alles auswählen

 
TMyClass(Items[i]).MyProperty := irgendwas;
 


schreiben.

MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

Re: [Erledigt] Noch eine Frage zu OOP Klassen mit Objektlist

Beitrag von MacWomble »

fliegermichl hat geschrieben:
MacWomble hat geschrieben:Das mit den Items als Default Property muss ich mir noch genauer ansehen, aber ich glaube das bringt mit keine wesentlichen Vorteile.


Der Vorteil ist, daß du nicht bei jedem Zugriff einen Typecast machen musst. Die Property Items von TObjectList sind vom Typ TObject. Wenn Items eine Liste von speziellen Klasseninstanzen hält, musst du ansonsten bei jedem Zugriff

Code: Alles auswählen

 
TMyClass(Items[i]).MyProperty := irgendwas;
 


schreiben.


Danke, ich habe das schon verstanden. Zunächst quäle ich mich noch mit anderen Grundlagen :shock:
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

Re: Noch eine Frage zu OOP Klassen mit Objektlisten

Beitrag von MacWomble »

... hab den Thread nochmal aktiviert:

Ich habe ja die Klasse TAdresse, welche Kontakte (TObjectlist) und Kontaktnummern (TObjectlist) enthält.
Diese Listen sollen mit der Adressinstanz erzeugt und zerstört werden. Hierzu habe ich folgenden Konstrukt, welcher aber
beim Aufruf von 'Adresse.free' in einem Assemblerfehler endet.
Wo habe ich mich diesmal geirrt? :oops: (möglich ist auch, dass ich den :evil: wieder ganz wo anders implementiert habe )

Code: Alles auswählen

{ TAdresse }
 
  TAdresse = class(TObject) 
 
private
  fKontaktnummern: TKontaktdatenListe;
  fKontakte: TKontaktListe; 
  ...
 
public
  constructor Create;
  destructor Destroy; override;
  property Kontaktnummern: TKontaktdatenListe read fKontaktnummern write fKontaktnummern;
  property Kontakte: TKontaktListe read fKontakte write fKontakte;
  ...
 
 
constructor TAdresse.Create;
begin
  Inherited create;
  fKontakte:=TKontaktListe.Create();
  fKontaktnummern:= TKontaktdatenListe.Create();
end;
 
destructor TAdresse.Destroy;
begin
  if assigned(fKontakte) then FreeAndNil(fKontakte);
  if assigned(fKontaktnummern) then FreeAndNil(fKontaktnummern);
  Inherited Destroy;
end;     
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1430
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Noch eine Frage zu OOP Klassen mit Objektlisten

Beitrag von fliegermichl »

Genauso ist es richtig. Der Fehler liegt definitiv woanders.
Setz mal einen Breakpoint in TKontaktdatenliste.Destroy. Vielleicht bringt das mehr Klarheit.

MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

Re: Noch eine Frage zu OOP Klassen mit Objektlisten

Beitrag von MacWomble »

Nochmals Danke. Es hat weiter geholfen, zu wissen, dass der Fehler nicht hier steckte.

Ursache waren vergessene Freigaben (also zu viele davon ;-) )
Das hängt damit zusammen, dass ich die Struktur veränderte.
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: [Erledigt] Noch eine Frage zu OOP Klassen mit Objektlist

Beitrag von Warf »

Übrigens der FPC kann valgrind code erzeugen. Valgrind ist ein mittelprächtiger Simulator (d.h. es ist nicht das schnellste) das alle Speicherzugriffe trackt. d.h. Wenn du ein Use after Free hast, Free vergisst, out of bounds array zugriff, oder sonstige Speicherfehler hast, wird valgrind dir das sagen, mit genauem Code angabe (z.B. bei Use after Free wird dir Valgrind sagen wo du es zunächst gefreed hast und wo es dann tatsächlich kracht).

Außerdem:

Code: Alles auswählen

  if assigned(fKontakte) then FreeAndNil(fKontakte);
  if assigned(fKontaktnummern) then FreeAndNil(fKontaktnummern);


Assigned ist in etwa sowas:

Code: Alles auswählen

function Assigned(p: Pointer): Boolean;
begin
  Result := p <> nil;
end;

FreeAndNil ist das:

Code: Alles auswählen

 
procedure FreeAndNil(var obj);
begin
  TObject(obj).free;
  pointer(obj):=nil;
end;

Und TObject.Free ist das

Code: Alles auswählen

procedure TObject.Free;
begin
  if self <> nil then self.destroy;
end;

Wenn wir das alles einmal inlinen steht da also in deinem konstruktor:

Code: Alles auswählen

if fKontakte <> nil then
begin
  if fKontakte <> nil then
    fKontakte.Destroy;
  fKontakte := nil;
end;

Zum einen ist das if assigned also völlig überflüssig, zum anderen musst du nicht FreeAndNil benutzen, denn das ist der Destruktor. Danach exsistieren die Felder eh nicht mehr. FreeAndNil muss man nur benutzen wenn man danach noch zugriff auf das Feld hat, damit man nicht versehentlich auf die alte Addresse schreibt.
Nimm doch also einfach die deutlich kürzere variante:

Code: Alles auswählen

  fKontakte.Free;
  fKontaktnummern.Free;

Hast keinen nachteil dardurch, außer das dein Code leserlicher wird

MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

Re: [Erledigt] Noch eine Frage zu OOP Klassen mit Objektlist

Beitrag von MacWomble »

Danke! :D
Muss ja nicht alles doppelt gemoppelt werden ... :oops:
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

Antworten