Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Konstruktoren in Delphi (https://www.delphipraxis.net/202004-konstruktoren-delphi.html)

Int3g3r 18. Sep 2019 08:51

Konstruktoren in Delphi
 
Guten Tag,

Kann mir jemand erklären wie die ganze Konstruktoren Geschichte in Delphi funktioniert ?
Ich blicke da einfach nicht durch.

Ich habe ein neues VCL-Form erstellt und möchte nun einen eigenen Konstruktor erstellen:

Delphi-Quellcode:
public
   { Public-Deklarationen }
   Constructor Create(_pnlColor:TColor;_pnlTitle:String;_memoContent:TStringList);

//....

constructor TFehler.Create(_pnlColor: TColor; _pnlTitle: String;
   _memoContent: TStringList);
begin
   Inherited Create(self); //1
   setPanelTitle(_pnlTitle);
   setPanelTitleColor(_pnlColor);
   fillMemo(_memoContent);
end;
1. Warum muss hier Create(self) angegeben werden ? Warum reicht Inherited; nicht aus ?

Ich verstehe das so: Inherited muss benutzt werden damit der Standard-Konstruktor aufgerufen wird. Wenn ich Inherited nicht verwenden würde, dann würde mein VLC-Formular nicht erstellt werden.

2. Wann werden folgende Schlüsselwörter benötigt.
  • override
  • overload
  • reintroduce
  • virtual

3. Wie erstelle ich einen eigenen Konstruktor ohne Parameter ?
Ich möchte noch bestimmte Werte setzten, wenn das Objekt erstellt wird.

Im Beispiel hier wird ein overload; angegeben.
Dies begreife ich nicht. Ein override; würde für mich mehr Sinn ergeben. Ich möchte ja einen eigenen Standard-Constructor erstellen sonst würden ja zwei gleiche exisieren:

Delphi-Quellcode:
type
  // Define a parent class, base on TObject by default
  TFruit = class
  public
    name  : string;
    Constructor Create; overload;  // This constructor uses defaults
    Constructor Create(name : string); overload;
  end;

constructor TFruit.Create;
begin
   Inherited;
        //Setze Initial-Werte...
end;
Somit existiert hier ja:
Delphi-Quellcode:
Create; //Standard-Konstruktor
Create; //Überladener-Konstruktor
Create(name:String); overload; //Überladener-Konstruktor was ok ist weil durch die parameter kann der Konstruktor vom anderen unterschieden werden
Wie weis Delphi nun welchen Create; Konstruktor genommen werden soll ?

Danke für die Hilfe.
Gruss Int3g3r

Sherlock 18. Sep 2019 08:57

AW: Konstruktoren in Delphi
 
Ich empfehle dringend ein Tutorial zur OOP mit Delphi.
Zur Detailfrage zum Thema Überladen:
Delphi erkennt die korrekte Methode anhand der Parametersignatur des Aufrufs. Das merkt man spätestens dann, wenn man mit zwei identischen Signaturen überladen möchte. Signatur ist hier übrigens eine vornehmes Wort für Parameterliste. Daraus folgt übrigens, daß die beiden Create-Aufrufe ohne Parameter nicht zwei verschiedene Methoden aufrufen werden, sondern beide die gleiche, das was da steht ist also falsch.

Sherlock

TiGü 18. Sep 2019 09:08

AW: Konstruktoren in Delphi
 
1. Wen TFehler ein Formular ist, dann gibst du mit inherited Create(...) den Owner an. Also die Komponente/Formular, was dieses Formular wieder freigeben soll.
Hier Self mitzugeben ist Quark.

2. Es gilt:
override - Überschreiben einer virtuellen Methode/Konstruktor/Destruktor;
overload - Ich habe mehrere gleichnamige Methoden, die sich anhand ihrer Parameter unterscheiden (keine, ein Parameter, bis N-Parameter).
reintroduce - Ich möchte, dass meine Klasse und seine Ableitungen diese Methode nehmen und nicht die gleiche Methode aus der Elternklasse.
virtual - Nachkommenklassen sollen diese Methode überschreiben (override) können und weiteres Verhalten ergänzen.

3. Genau so wie, wie du es im verlinkten Beispiel siehst:
Delphi-Quellcode:
type
  // Define a parent class, base on TObject by default
  TFruit = class
  public
    name  : string;
    Constructor Create; overload;  // This constructor uses defaults
    Constructor Create(name : string); overload;
  end;

...

// Create a fruit object - parameterless version
constructor TFruit.Create;
begin
  // Execute the parent (TObject) constructor first
  inherited; // Call the parent Create method

  // Now set a default fruit name
  self.name := 'Fruit';              <--------------------------- Da kommt dein "Ich möchte noch bestimmte Werte setzten, wenn das Objekt erstellt wird."
end;
4. Ohne Argumente wird der überladene Konstruktor der Klasse .genommen

Int3g3r 18. Sep 2019 09:30

AW: Konstruktoren in Delphi
 
Zitat:

1. Wen TFehler ein Formular ist, dann gibst du mit inherited Create(...) den Owner an. Also die Komponente/Formular, was dieses Formular wieder freigeben soll.
Hier Self mitzugeben ist Quark.
Ja, TFehler ist eine VLC-Formular. Mir ist einfach nicht klar was ich beim Owner angeben soll.
Dies ist ein eigenständiges Formular, also es hat keinen Owner.

Danke für die Hilfe

Schokohase 18. Sep 2019 09:49

AW: Konstruktoren in Delphi
 
Wenn es keinen Owner hat, dann ist der Owner wohl
Delphi-Quellcode:
nil
.

Int3g3r 18. Sep 2019 10:18

AW: Konstruktoren in Delphi
 
Vielen Dank an alle.

Ich habe jetzt meine Konstruktoren wie im Beispiel erstellt. Lässt sich aber nicht kompillieren.
Wo liegt mein Fehler ?

Delphi-Quellcode:
public
      { Public-Deklarationen }
      Constructor Create; overload;
      Constructor Create(_pnlColor:TColor;_pnlTitle:String;_memoContent:TStringList); overload;
   end;

//.......

constructor TFehler.Create(_pnlColor: TColor; _pnlTitle: String;_memoContent: TStringList);
begin
   Inherited Create(nil);
   setPanelTitle(_pnlTitle);
   setPanelTitleColor(_pnlColor);
   fillMemo(_memoContent);
end;

constructor TFehler.Create;
begin
   Inherited; // Fehler E2008
end;
Meldungen/Fehler beim kompilieren:
[dcc32 Warnung] form_CSVException.pas(24): W1010 Methode 'Create' verbirgt virtuelle Methode vom Basistyp 'TCustomForm'
[dcc32 Warnung] form_CSVException.pas(25): W1010 Methode 'Create' verbirgt virtuelle Methode vom Basistyp 'TCustomForm'
[dcc32 Fehler] form_CSVException.pas(53): E2008 Inkompatible Typen

- Warum erhalte ich den Fehler E2008 ? Wie lässt sich dieser beheben ?
- Wie lassen sich die Warungen beheben ?

Schokohase 18. Sep 2019 10:22

AW: Konstruktoren in Delphi
 
Probier es mal so
Delphi-Quellcode:
public
      { Public-Deklarationen }
      Constructor Create; reintroduce; overload;
      Constructor Create(_pnlColor:TColor;_pnlTitle:String;_memoContent:TStringList); reintroduce; overload;
   end;

//.......

constructor TFehler.Create;
begin
   Inherited Create(nil);
end;

constructor TFehler.Create(_pnlColor: TColor; _pnlTitle: String;_memoContent: TStringList);
begin
   Create(); // ruft TFehler.Create() auf
   setPanelTitle(_pnlTitle);
   setPanelTitleColor(_pnlColor);
   fillMemo(_memoContent);
end;

Int3g3r 18. Sep 2019 10:28

AW: Konstruktoren in Delphi
 
Zitat:

Zitat von Schokohase (Beitrag 1446841)
Probier es mal so

... So funktioniert es!

Warum funktioniert es nicht wie es im Beispiel steht ?
Was ist mit den Warnugen ?

Danke!

Schokohase 18. Sep 2019 10:33

AW: Konstruktoren in Delphi
 
Es funktioniert so wie im Beispiel, soweit das Beispiel hier auch zutrifft.
Welche Warnungen? Es dürften keine Warnungen mehr da sein.

dataspider 18. Sep 2019 10:47

AW: Konstruktoren in Delphi
 
Zitat:

Zitat von Int3g3r (Beitrag 1446832)
Zitat:

1. Wen TFehler ein Formular ist, dann gibst du mit inherited Create(...) den Owner an. Also die Komponente/Formular, was dieses Formular wieder freigeben soll.
Hier Self mitzugeben ist Quark.
Ja, TFehler ist eine VLC-Formular. Mir ist einfach nicht klar was ich beim Owner angeben soll.
Dies ist ein eigenständiges Formular, also es hat keinen Owner.

Den Parameter AOwner wegzulassen halte ich für keine gute Idee.
Du musst dich dann zwingend selbst um die Freigabe kümmern.
Nur wenn dein Form (TFehler) immer modal in einen try finally mit Freigabe aufgerufen wird, könnte man es akzeptieren.
Machen würde ich es dennoch nicht. Bei mir haben alle Konstruktoren eines TForms alse ersten Parameter AOwner...
Der Aufruf zum Erzeugen kann dann TFehler.Create(Nil, Color...) oder TFehler.Create(Application, Color...) etc. sein.

Frank

Dennis07 20. Sep 2019 15:22

AW: Konstruktoren in Delphi
 
Zitat:

Zitat von dataspider (Beitrag 1446851)
Den Parameter AOwner wegzulassen halte ich für keine gute Idee.
Du musst dich dann zwingend selbst um die Freigabe kümmern.
Nur wenn dein Form (TFehler) immer modal in einen try finally mit Freigabe aufgerufen wird, könnte man es akzeptieren.
Machen würde ich es dennoch nicht. Bei mir haben alle Konstruktoren eines TForms alse ersten Parameter AOwner...
Der Aufruf zum Erzeugen kann dann TFehler.Create(Nil, Color...) oder TFehler.Create(Application, Color...) etc. sein.

Frank

Oder im Destructor freigeben.
Oder im OnDestroy freigeben.
Das wären alles gültige Alternativen.

Int3g3r 21. Sep 2020 14:11

AW: Konstruktoren in Delphi
 
Besten Dank für die Antworten.

Leider blicke ich immer noch nicht durch bei den Konstruktoren/Destruktoren.

Z.b
Delphi-Quellcode:
type
  TMail = Class
    smtp: TIdSMTP;
    sslIOHandler: TIdSSLIOHandlerSocketOpenSSL;

    private
    // Private
    public
    // Public
    constructor Create(param1, param2 : String);
    destructor Destroy;
  end;

  var
  Mail : TMail;

implementation

constructor TMail.Create(param1, param2 : String);
begin
  smtp := TIdSMTP.Create(nil);
  sslIOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
end;

destructor TMail.Destroy;
begin
  if Assigned(smtp) then
      FreeAndNil(smtp);
  if Assigned(sslIOHandler) then
      FreeAndNil(sslIOHandler);
end;
4) Wann ist ein "Inherited" notwenig im Konstruktor/Dekonstruktor ? Sobald von einer Klasse geerbt wird, richtig ?
Delphi-Quellcode:
TMail = Class(TComponent) // Hier währe ein Inherited dann notwendig, richtig ?
.

5) Wie bei meinem Beispiel zu sehen habe ich keinen "Standard-Konstruktor" erstellt.
Meine Klasse soll nur mit diesem Konstruktor mit Parameter instanziert werden können.
Bei C# ist ein Standardkonstruktor notwendig, ist das bei Delphi auch der Fall ?

6) Das Beispiel oben lässt sich kompillieren und instanzieren.
Leider verstehe ich aber nicht warum bei
Delphi-Quellcode:
Mail.Free
der Destruktor nicht abgehandelt wird.
Ich erzeuge Memory-Leaks, was mache ich falsch ?

Lösung zu 6)
Delphi-Quellcode:
destructor Destroy; override;

Gruss Int3g3r

PS: Um Memory-Leaks zu Prüfen folgende Zeile beim Projekt->Quelltext einfügen
Delphi-Quellcode:
ReportMemoryLeaksOnShutdown := True;

Dalai 21. Sep 2020 14:34

AW: Konstruktoren in Delphi
 
Zitat:

Zitat von Int3g3r (Beitrag 1473984)
4) Wann ist ein "Inherited" notwenig im Konstruktor/Dekonstruktor ? Sobald von einer Klasse geerbt wird, richtig ?

Setze einfach inmmer ein
Delphi-Quellcode:
inherited;
in den Konstruktor und Destruktor, bei ersterem (in aller Regel) am Anfang, im Destruktor ans Ende. Jede Klasse erbt von irgendeiner Basisklasse, und wenn es nur TObject ist, die zwar einen leeren Konstruktor hat, aber vielleicht ändert sich das mal.

Zitat:

Meine Klasse soll nur mit diesem Konstruktor mit Parameter instanziert werden können.
Das geht auch irgendwie, im Zweifel mit Prüfung bestimmter Variablen beim Aufruf anderer Methoden der Klasse. Will man den Konstruktor der Basisklasse verdecken und dem Compiler sagen, dass das so in Ordnung ist, kann man ihn mit
Delphi-Quellcode:
overload; reintroduce;
markieren. IIRC verhindert das aber nicht, den Konstruktor der Basisklasse zu nutzen.

Zitat:

Bei C# ist ein Standardkonstruktor notwendig, ist das bei Delphi auch der Fall ?
Jein. Es gibt immer den Konstruktor der Basisklasse, der immer dann verwendet wird, wenn in der abgeleiteten Klasse keiner definiert ist oder keiner mit passender Parametersignatur gefunden wird. Aber der Konstruktor der Basisklasse lässt nicht immer anwenden, z.B. bei abstrakten Klassen (TStrings).

Zitat:

6) Das Beispiel oben lässt sich kompillieren und instanzieren.
Leider verstehe ich aber nicht warum bei
Delphi-Quellcode:
Mail.Free
der Destruktor nicht abgehandelt wird.
Jeder Destruktor sollte/muss mit
Delphi-Quellcode:
override;
markiert werden, damit der der abgeleiteten Klasse gerufen wird, nicht der der Basisklasse. Tut man das nicht, gibt der Compiler eine Warnung, soweit ich weiß.

Grüße
Dalai

himitsu 21. Sep 2020 16:30

AW: Konstruktoren in Delphi
 
Jupp, beim DESTRUCTOR immer das OVERRIDE.

Teilweise auch beim CONSTRUCTOR.
* siehe TComponent, wenn die Komponente zur DesignTime auf die Form soll, denn der DFM-Loader kennt nur "sein" Create.

* Es gibt zwar einen leeren Standard-Constructor in TObject, aber der muss nicht aufgerufen werden. (besser ist es aber, wenn er wird)
* * denn aktuell ist der eh leer
* * VOR dem Create (oder wie auch immer man das nennen mag) wird bei einem "CONSTRUCTOR" immer erstmal die Speicherinitialisierung durchgeführt, also vor dem Inhalt dies Create

INHERITED einach immer.
* wenn es nicht nötig ist, weil es nichts im Vorfahren gibt, dann ignoriert es der Compiler
* ansonsten ruft es die "gleichnamige" Methode mit den "gleichen" Parametern im Vorfahren auf (bzw. die Methode mit dem Namen den man angibt)

Am Einfachsten/Sinnvollsten ist es das INHERITED bei "erstellenden" Methoden (Create/Get/...) als Erstes auszuführen
und bei löschenden/freigebenden Methoden (Destroy/Delete/...) als Letztes. (Ausnahmen gibt es, aber meistens ist es so am Besten)

Im Delphi gibt es immer per Standard ein "leeres" Create.
* Wenn man nichts initialisieren will, dann braucht man also keinen Eigenen
* genauso beim DESCRUCTOR (hier gibt es im Delphi nur den "Destroy", welcher automatisch aufgerufen wird)
* * hat man nichts freizugeben, oder alles wird automatisch freigegeben (z.B. String, dyn. Array, Interface oder Variant), dann braucht man hier auch keinen eigenen DESTRUCTOR



Im Free/FreeAndNil ist bereits ein "if Assigned" eingebaut, also nochmal ist nicht nötig. (doppelt ist nicht falsch, nur unnötig)

Delphi-Quellcode:
type
  TMail = class(TComponent)
  ...

constructor TMail.Create(param1, param2 : String);
begin
  inherited Create(nil); // NIL: oder du baust den Owner auch in dein Create mit ein
  smtp := TIdSMTP.Create(Self);
  sslIOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(Self);
end;
Hier wird automatisch Speicherverwaltet, daher ist für diese beiden Felder/Variablen kein Destructor nötig.

Das Inherite bei diesem Creaate(nil) ist nicht unbedingt nötig.
* mit inherited wird immer das Create(mit TComponent) im Vorfahren aufgerufen
* ohne inherited in deiner Klasse (sollte es dort so ein Create geben), ansonsten im Vorfahren

Int3g3r 22. Sep 2020 11:14

AW: Konstruktoren in Delphi
 
Danke für die weiteren Antworten.

Zitat:

Setze einfach inmmer ein inherited; Jede Klasse erbt..
Also wenn ich
Delphi-Quellcode:
TMail = Class
schreibe erbt diese automatisch von TObject ?

Zitat:

Im Free/FreeAndNil ist bereits ein "if Assigned" eingebaut, also nochmal ist nicht nötig. (doppelt ist nicht falsch, nur unnötig)
Danke für den Hinweis das wusste ich nicht.

Nun habe ich noch eine weitere Frage.
Wie lässt sich der Owner in meinem Beispiel einbauen, bzw macht dies überhaupt Sinn ?
Delphi-Quellcode:
constructor TMail.Create(AOwner: TComponent, param1, param2 : String);
begin
  inherited Create;
  smtp := TIdSMTP.Create(nil);
  sslIOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
end;
Ich kann bei
Delphi-Quellcode:
inherited Create;
keinen Owner angeben da meine Klasse von TObject erbt.
Das Create von TObject will keine Parameter.
Bzw was soll ich danach mit dem Owner anstellen wenn ich diesen im TMail.Create übergebe ?

Gruss Int3g3r.

Dalai 22. Sep 2020 12:02

AW: Konstruktoren in Delphi
 
Zitat:

Zitat von Int3g3r (Beitrag 1474026)
Also wenn ich
Delphi-Quellcode:
TMail = Class
schreibe erbt diese automatisch von TObject ?

So ist es. TObject ist in der Klassenhierarchie ganz oben, sozusagen der Urvater aller Objekte.

Delphi-Quellcode:
TMail = Class
ist dasselbe wie
Delphi-Quellcode:
TMail = Class(TObject)
.

Zitat:

Wie lässt sich der Owner in meinem Beispiel einbauen, bzw macht dies überhaupt Sinn ?
Delphi-Quellcode:
constructor TMail.Create(AOwner: TComponent, param1, param2 : String);
begin
  inherited Create;
  smtp := TIdSMTP.Create(nil);
  sslIOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
end;
Ich kann bei
Delphi-Quellcode:
inherited Create;
keinen Owner angeben da meine Klasse von TObject erbt.
Das Create von TObject will keine Parameter.
Bzw was soll ich danach mit dem Owner anstellen wenn ich diesen im TMail.Create übergebe ?
Einen Owner gibt es erst bei TComponent. Entweder leitest du davon ab, oder du musst den Owner-Parameter weglassen. Kommt darauf an, was deine Klasse tun soll.

Grüße
Dalai


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