AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren

Aufruf neuer Typen in anderen Units

Offene Frage von "DeddyH"
Ein Thema von Caesar2012 · begonnen am 31. Jan 2012 · letzter Beitrag vom 1. Feb 2012
Antwort Antwort
Caesar2012

Registriert seit: 25. Jan 2012
6 Beiträge
 
FreePascal / Lazarus
 
#1

Aufruf neuer Typen in anderen Units

  Alt 31. Jan 2012, 14:14
Aufruf neuer Typen in anderen Units


Einleitung:
Ich bin mit der Weile ein fortgeschrittener Anfänger. Zwar kann ich neue Methode, Variablen und Formen miteinander Verknüpfen, aber wenn ich manchmal meine Fehler sehe, lache ich später selbst darüber. Nun geht es darum, eine neue Unit zu erstellen (schon geschehen) und einen neuen Typen zu kreieren. Ich kann darauf zugreifen, bekomme dennoch einen "SIGSEGV"-Error, einen Zugriffserror. Ich suche nach der Lösung seit 3 Tagen und arbeitete mich durch sämtliche Anleitungen, Themen, Foren und was es alles noch so gibt. Arbeiten tue ich mit Lazarus "0.9.30.2" und Win7 32bit.


Überblick:
Mein Projekt beinhaltet die Formen Form1 vom Typ TForm1 in mainunit.lfm bzw. mainunit.pas und Form2 vom Typ TForm2 in image.lfm bzw. image.pas. Außerdem beinhaltet mein Projekt die Unit kennzeichen.pas in denen TListe und TDatensatz deklariert sind.


Problembeschreibung:
In meiner mainunit.pas befindet sich procedure TForm1.Search(SearchString: String); , welche von einer anderen Prozedur in mainunit.pas aufgerufen wird. Am Schluss des interface-Teils deklariere ich:
Delphi-Quellcode:
var
     Form1: TForm1;
     isGo: Boolean;
     unsereListe: TListe;
Die Prozedur "Search" in mainunit.passieht im "implementation"-Teil wie folgt aus:
Delphi-Quellcode:
procedure TForm1.Search(SearchString: String);
begin
     if (Length(SearchString) <> 0) and (SearchString <> Last01.Caption) then
     begin
          Form1.Enabled := False;
          StartProgressBar(25);
          Wait(3);
          ChangeLast(SearchString);
          unsereListe := unsereListe.create;
          unsereListe.dateieinlesen('kfz.csv', SearchString);
          if unsereListe.NotA = false then
          begin
               Output2.Caption := unsereListe.liste[unsereListe.anzahl].kennzeichen;
               Output4.Caption := unsereListe.liste[unsereListe.anzahl].ort;
               Output6.Caption := unsereListe.liste[unsereListe.anzahl].bundesland;
               Output8.Caption := unsereListe.liste[unsereListe.anzahl].sonder;
          end;
          while ProgressBar.Position <> 0 do Wait(2);
          Form1.Enabled := True;
     end;
end;
Durch das Durchsteppen mit F7 erfuhr ich, dass er wie befohlen
Delphi-Quellcode:
constructor TListe.create;
begin
     anzahl := 0;
     NotA := false;
end;
ohne Probleme durchläuft und erst bei
Delphi-Quellcode:
procedure TListe.dateieinlesen (datname, suchKFZ: STRING);
begin
      try
           datname := extractfilepath(ParamStr(0)) + 'lib\kfz.csv';
           CompleteFile := TStringList.Create;
           CompleteFile.LoadFromFile(datname);
           ...
      finally
           ...
      end;
end;
kläglich scheitert. Nach dem "SIGSEGV"-Error zeigt er mit die Zeile CompleteFile := TStringList.Create; an. Das heißt, er kommt erst gar nicht dazu, CompleteFile.LoadFromFile(datname); durchzuführen.

Ich habe mein Projekt auf file-upload.net hochgeladen.

P.S.:

Während ich das Thema erstellt habe, fiel mir etwas auf. Ich verschob in kennzeichen.pas CompleteFile := TStringList.Create; in die constructor TListe.create; . Schon leif dies ohne Probleme. Aber es stockte nun bei CompleteFile.LoadFromFile(datname); .




Mit freundlichen Grüßen
Caesar2012, der Moderne
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#2

AW: Aufruf neuer Typen in anderen Units

  Alt 31. Jan 2012, 14:30
Versuch mal:
Delphi-Quellcode:
constructor TListe.create;
begin
  inherited; // <--- das ist wichtig!

     anzahl := 0;
     NotA := false;
end;
EDIT: Du kannst auch an deinen Beitrag Dateien anhängen, dann bleiben die auch am Beitrag und verschwinden nicht, wenn der Hoster sich verabschiedet
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Caesar2012

Registriert seit: 25. Jan 2012
6 Beiträge
 
FreePascal / Lazarus
 
#3

AW: Aufruf neuer Typen in anderen Units

  Alt 31. Jan 2012, 15:05
Danke für die schnelle Antwort. Was macht dieses inherited; ?
Ich könnte das Projekt nicht anhängen, da es 17 MB groß war. Und in den nächsten Monaten verabschiedet er sich nicht, der ist seit Jaaahren online und mein Favorit, da ohne Schnickschnack.

EDIT: Es funktioniert immer noch nicht. Nach dem Error meldet mir der Debugger beim Überfahren von CompleteFile := TStringList.Create; in TListe.dateieinlesen

"TStringList.Create = A syntax error in expression near `.Create` .

Geändert von Caesar2012 (31. Jan 2012 um 15:15 Uhr) Grund: Erweiterung
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#4

AW: Aufruf neuer Typen in anderen Units

  Alt 31. Jan 2012, 15:13
Danke für die schnelle Antwort. Was macht dieses inherited; ?
inherited sorgt dafür, dass der Code aus der Vorgänger-Klasse für diese Methode (bzw. hier Konstruktor) auch aufgerufen wird.
Ich könnte das Projekt nicht anhängen, da es 17 MB groß war.
Du hast da eine Software mit 17MB gezippten QuellText ... Respekt ... das nenne ich fleißig ... ich kann es aber irgendwie nicht richtig glauben
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Caesar2012

Registriert seit: 25. Jan 2012
6 Beiträge
 
FreePascal / Lazarus
 
#5

AW: Aufruf neuer Typen in anderen Units

  Alt 31. Jan 2012, 15:51
Wie schon oben gesagt, funktioniert es immer noch nicht. Danke für deine Antwort. Jetzt habe ich nur das nötigste und ohne Bilder als Anhang mein Projekt hinzugefügt.

MFG Caesar2011, der Moderne

Falls ihr noch was benötigt, sagt es ruhig.
Angehängte Dateien
Dateityp: zip Zipper.zip (2,53 MB, 4x aufgerufen)
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#6

AW: Aufruf neuer Typen in anderen Units

  Alt 31. Jan 2012, 17:05
Sodele, ich habe dann mal die Unit entsprechend kommentiert und ausgebessert
Delphi-Quellcode:
unit kennzeichen;

// {$mode objfpc}{$H+}

interface

uses
  SysUtils,
  Classes;
// , Graphics, Controls, Forms, Dialogs, ExtCtrls;

type
  TDatensatz = class
    kennzeichen : STRING;
    // Öffentliche Variable, die das "kennzeichnen" ausgibt
    Ort : STRING;
    // Öffentliche Variable, die den "ort" ausgibt
    Bundesland : STRING;
    // Öffentliche Variable, die das "bundesland" ausgibt
    Sonder : STRING;
    // Öffentliche Variable, die das "bundesland" ausgibt
    constructor Create;
    // Konstruktor von TDatensatz
    destructor Destroy;
    // Destruktor von TDatensatz
    procedure Einlesen( DatenString : STRING );
    // Aufteilen von "datenstring" in "kennzeichen", "ort", "bundesland"
    function IstKennzeichen( KennzeichenString : STRING ) : BOOLEAN;
    // Boolean'sche Funktion, ob "kennzeichenstring" == "kennzeichen"
  end;

  TListe = class
  public
    Liste : ARRAY [1 .. 600] OF TDatensatz;
    // riesiges Array der kompletten *.csv des Types "TDatensatz"
    Anzahl : CARDINAL;
    // Nr. der aktuellen Zeile in der *.csv
    CompleteFile : TStringList;
    // Ganzes File als TStringList
    NotA : BOOLEAN;
    // Sagt aus, ob Input verfügbar
    constructor Create;
    // Konstruktor von TListe
    destructor Destroy; override;
    // Destruktor von TListe
    procedure DateiEinlesen( datname, suchKFZ : STRING );
    // Einlesen und Verarbeiten der *.csv (LoadFileFrom, Erstellen von "liste")
    function Lesen( i : CARDINAL; datnamen : TStringList ) : TDatensatz;
    // Gibt einen result des Typen TDatensatz aus

    function GibAnzahl : CARDINAL;
    // Gibt Variable "anzahl" aus

    // aber wozu? die Variable kann man doch direkt auslesen, wozu nochmals eine function dafür?

    function NAcheck( j : INTEGER; Stringlist : TStringList ) : BOOLEAN;
  end;

var
  unsereListe : TListe;

implementation

constructor TDatensatz.Create;
begin
  inherited; // <-- fehlte
  kennzeichen := '';
  Ort := '';
  Bundesland := '';
  Sonder := '';
end;

destructor TDatensatz.Destroy;
begin
  // überflüssig

  // kennzeichen := '';
  // Ort := '';
  // Bundesland := '';
  // Sonder := '';
  inherited; // <-- fehlte
end;

procedure TDatensatz.Einlesen( DatenString : STRING );
begin
  kennzeichen := Copy( DatenString, 1, pos( ';', DatenString ) - 1 );
  Delete( DatenString, 1, pos( ';', DatenString ) );
  Ort := Copy( DatenString, 1, pos( ';', DatenString ) - 1 );
  Delete( DatenString, 1, pos( ';', DatenString ) );
  Bundesland := DatenString;
end;

function TDatensatz.IstKennzeichen( KennzeichenString : STRING ) : BOOLEAN;
begin
  Result := false; // <-- verschoben, da sonst immer False als Result
  if KennzeichenString = kennzeichen
  then
    Result := true;
  // result := false;
end;

constructor TListe.Create;
begin
  inherited;
  Anzahl := 0;
  NotA := false;
  CompleteFile := TStringList.Create; // das muss hier hin
end;

destructor TListe.Destroy;
var
  i : CARDINAL;
begin
  if Anzahl > 0
  then
    begin
      for i := 1 to Anzahl do
        Liste[i].Free; // nicht Destroy direkt aufrufen
      Anzahl := 0;
    end;
  NotA := false;
  CompleteFile.Free; // nicht Destroy direkt aufrufen
  inherited; // <-- fehlte
end;

procedure TListe.DateiEinlesen( datname, suchKFZ : STRING );
begin
  // try

  datname := extractfilepath( ParamStr( 0 ) ) + 'lib\kfz.csv';

  // Nein, da gibt es schon eine Instanz

  // CompleteFile := TStringList.Create;

  CompleteFile.LoadFromFile( datname );
  if NAcheck( Anzahl, CompleteFile )
  then
    begin
      Lesen( Anzahl, CompleteFile );
      if not Liste[Anzahl].IstKennzeichen( suchKFZ )
      then
        begin
          Anzahl := GibAnzahl + 1;
          DateiEinlesen( datname, suchKFZ );
        end
      else
        begin
          if Lesen( Anzahl, CompleteFile ).Bundesland = ''
          then
            begin
              Liste[Anzahl].Sonder := Liste[Anzahl].Bundesland;
              Liste[Anzahl].Bundesland := '';
              Liste[Anzahl].Ort := '';
            end;
        end;
    end;

  // und diese Instanz bleibt

  // finally
  // CompleteFile.Free;
  // end;
end;

function TListe.Lesen( i : CARDINAL; datnamen : TStringList ) : TDatensatz;
begin
  Liste[i].Einlesen( datnamen.Strings[i] );
  Result := Liste[i];
end;

function TListe.GibAnzahl : CARDINAL;
begin
  Result := Anzahl;
end;

function TListe.NAcheck( j : INTEGER; Stringlist : TStringList ) : BOOLEAN;
begin
  Result := false; // <-- verschoben, da sonst immer False als Result
  if j <= Stringlist.Count
  then
    Result := true;
  NotA := true;
  // Result := false;
end;

end.
Allerdings ist der wirkliche Fehler gar nicht in der Unit zu finden
sondern hier:
Delphi-Quellcode:
procedure TForm1.Search( SearchString : String );
begin
  if ( Length( SearchString ) <> 0 ) and ( SearchString <> Last01.Caption )
  then
    begin
      Form1.Enabled := False;
      StartProgressBar( 25 );
      Wait( 3 );
      ChangeLast( SearchString );

      // hier war der Haupt-Fehler

      unsereListe := TListe.Create; // statt unsereListe.create

      try

        unsereListe.dateieinlesen( 'kfz.csv', SearchString );
        if not unsereListe.NotA // statt unsereListe.NotA = False
        then
          begin
            Output2.Caption := unsereListe.liste[unsereListe.anzahl].kennzeichen;
            Output4.Caption := unsereListe.liste[unsereListe.anzahl].ort;
            Output6.Caption := unsereListe.liste[unsereListe.anzahl].bundesland;
            Output8.Caption := unsereListe.liste[unsereListe.anzahl].sonder;
          end;

      finally
        unsereListe.Free; // freigeben, sonst gibt das ein riesen Speicherleck
      end;

      // Geduldsprobe für den User, es passiert nichts, ausser dass der Balken sich bewegt

      while ProgressBar.Position <> 0 do
        Wait( 2 );

      Form1.Enabled := True;
    end;
end;
  • Warum ist unsereListe global definiert? so wie du diese benutzt könntest du die auch lokal in der Methode deklarieren. Oder in der Form-Klasse ... aber so passt das nicht so richtig zusammen
  • Wofür du aber extra die Ausführung abbremst um da in einer ProgressBar etwas anzuzeigen, ist mir etwas schleierhaft.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Caesar2012

Registriert seit: 25. Jan 2012
6 Beiträge
 
FreePascal / Lazarus
 
#7

AW: Aufruf neuer Typen in anderen Units

  Alt 31. Jan 2012, 17:21
Danke, dass du dir so viel Zeit gelassen hast. Ich werde es begutachten!!

Gibt es hier im Forum ein "User-Like"-Buuton?

Bei den ganzen Fehlern wird mir schwindelig.

Den destructor TDatensatz.destroy; und alles andere mit destroy; habe ich ausder Schule, wo wie das Projetzt aus Zeitgründen niedergelegt haben.

Ach und zur Geduldsprobe: Weil ich's kann!


EDIT:
Jetzt bekomme ich zumindest kein Error. Nur leider kommt kein Output auf dem Feld. Ich schau mir mal den Debugger an.

Geändert von Caesar2012 ( 1. Feb 2012 um 10:35 Uhr) Grund: Update
  Mit Zitat antworten Zitat
Blup

Registriert seit: 7. Aug 2008
Ort: Brandenburg
1.429 Beiträge
 
Delphi 10.4 Sydney
 
#8

AW: Aufruf neuer Typen in anderen Units

  Alt 1. Feb 2012, 09:17
Delphi-Quellcode:
TDatensatz = class
{...}
  destructor destroy; override;
{...}
end;
Der Destructor Destroy muss mit "override" überschrieben werden, damit dieser auch von Free benutzt wird.
In dieser Klasse ist der constructor überflüssig, da Stringvariablen einer Klasse automatisch leer, Interfaces und Pointer nil und numerische Variablen mit 0 initialisiert werden.
Diese Hinweise gelten z.T. auch für deine anderen Klassen.

Liste : ARRAY [1 .. 600] OF TDatensatz; Sinnvoller ist hier ein dymamisches Array oder TObjectList.

Delphi-Quellcode:
// String-Parameter nach Möglichkeit als const übergeben, kann der Compiler besser optimieren.
function TDatensatz.istKennzeichen(const kennzeichenstring:STRING):BOOLEAN;
begin
// Vergleiche ergeben bereits einen Boolean-Wert
// if (True) then
// Result := True
// else
// Result := False;
// den man auch einer Variablen oder dem Rückgabewert direkt zuweisen kann.
  Result := (kennzeichenstring = kennzeichen);
end;
Das Laden und Speichern direkt im Datenobjekt zu implementieren ist keine gute Idee.
Hier bietet sich z.B. das Visitor-Pattern an.
http://de.wikipedia.org/wiki/Visitor
  Mit Zitat antworten Zitat
Caesar2012

Registriert seit: 25. Jan 2012
6 Beiträge
 
FreePascal / Lazarus
 
#9

AW: Aufruf neuer Typen in anderen Units

  Alt 1. Feb 2012, 10:46
Danke auch dir. Nur bei dem destroy von destructor TListe.destroy; hasbe ich Probleme.

Aufgerufen wird er mit unsereListe.destroy; in mainunit.pas, nachdem alles fertig ist.

Ich glaube es hat teilweise auch was mit dem anderen Destruktor destructor TDatensatz.destroy; zu tun. Sir Rufo half mir schon per PN, aber ich konnte keine wiederholte Zerstörung feststellen. Außerdem bin ich mir mit dem override sowie dem Unterschied von free/freeandnil/destoy nicht im Klaren. Wann benutze ich override: bei Destuktoren und wann nicht? Wann benutze ich .Free , .FreeAndNil und wann .Destroy ?

Delphi-Quellcode:
destructor TListe.destroy;
var i:CARDINAL;
begin
     if anzahl > 0 then
     begin
          for i := 1 to anzahl do liste[i].free;
          anzahl := 0;
     end;
     NotA := false;
     CompleteFile.free;
     inherited;
end;
Delphi-Quellcode:
destructor TDatensatz.destroy;
begin
     kennzeichen := '';
     ort := '';
     bundesland := '';
     sonder := '';
     inherited;
end;
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.537 Beiträge
 
Delphi 11 Alexandria
 
#10

AW: Aufruf neuer Typen in anderen Units

  Alt 1. Feb 2012, 10:50
Der Destruktor Destroy sollte immer überschrieben werden. Ein direkter Aufruf ist zu vermeiden, stattdessen Free verwenden, welches vorher auf nil prüft. FreeAndNil verwendet man dann, wenn es möglich sein kann, dass nach dem Freigeben evtl. noch auf die (nun nicht mehr vorhandene) Instanz zugegriffen werden könnte. Somit kann man auf nil prüfen.
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 17:12 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