Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   liege ich richtig mit dem OOP-Versuch (https://www.delphipraxis.net/191288-liege-ich-richtig-mit-dem-oop-versuch.html)

EdAdvokat 29. Dez 2016 16:54

Delphi-Version: 5

liege ich richtig mit dem OOP-Versuch
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo zusammen, ich bin in Fragen OOP noch nicht so sicher und bitte um eine Meinungsäußerung, ob ich mit einem kleinen Testprogramm richtig liege.
Es handelt sich um eine einfache Flächenberechnung als Beispiel. Mir geht es in erster Linie darum, ob ich die Getter und Setter sowie die propertys richtig
eingesetzt habe und ob man noch mehr in private verstecken könnte. Darauf würde ich dann aufbauen, um meine Möglichkeiten zu erweitern.
Ein Ok würde auch genügen. Danke

Der schöne Günther 29. Dez 2016 17:07

AW: liege ich richtig mit dem OOP-Versuch
 
Die Methoden auf welche die Properties zugreifen müssen nicht public sein, die können auch private oder protected sein.

Fritzew 29. Dez 2016 17:17

AW: liege ich richtig mit dem OOP-Versuch
 
Hallo

Es ist eine ganz schlechte Idee den Destructor Free zu nennen.
Free ist eine Methode von TObject die überprüft ob Self <> nil ist und dann den destructor Destroy aufruft.


Benenne Deinen Destructor um zu:
Interface

Delphi-Quellcode:
destructor Destroy; override;

Implementation

Delphi-Quellcode:
destructor TFlaecheninh.Destroy;
begin
  inherited Destroy;
end;
Dein Code ruft sich selber auf in Deinem destructor Free!!!

EdAdvokat 29. Dez 2016 17:44

AW: liege ich richtig mit dem OOP-Versuch
 
Danke für die schnellen Hinweise. Die Veränderung Destroy habe ich vorgenommen. Muss ich wirklich immer in derartigen Programmen einen destructor aufrufen?
Mehrfach habe ich gesehen, dass darauf verzichtet wurde. Wann und unter welchen Voraussetzungen muss ich den Speicher wirklich wieder frei geben?
Die Methoden, auf die die Propertys zugreifen (hier setLaenge, setBreite und GetFlaecheninhalt) lassen sich nicht in private oder protect verstecken.
Wie sollte ich also diese Methoden besser verstecken können ähnlich wie die private-Felder und Methoden FLaenge, FBreite, FFlaecheninhalt; function GetLaenge;
und function GetBreite?

EdAdvokat 29. Dez 2016 18:04

AW: liege ich richtig mit dem OOP-Versuch
 
Hallo Fritzew, in meinen Studien bin ich auf die Seite gestoßen: https://www.delphi-treff.de/object-p...n-und-objekte/
da wird u.a. zu Klassen und Objecte beispielhaft aufgeführt:

"...Beispiel: Eine Klassenmethode „simuliert“ eine Klassenvariable und zählt, wie viele Objekte es von einer Klasse gibt:
Delphi-Quellcode:
Unit Flaeche;

Interface

Type
  TFlaeche = class(TObject)
  private
  protected
    Function BerechneFlaeche: Double; virtual; abstract;
    Function BerechneUmfang: Double; virtual; abstract;
  public
    Constructor Create;
    Destructor Free;
    Class function count: Integer;
    Property Flaeche: Double read BerechneFlaeche;
    Property Umfang: Double read BerechneUmfang;
  End;

Implementation

Var anzahl: Integer;

Constructor TFlaeche.Create;
Begin
  Inc(anzahl);
End;

Destructor TFlaeche.Free;
Begin
  Dec(anzahl);
End;

Class Function TFlaeche.Count: Integer;
Begin
  result := anzahl;
End;

End.
Im Konstruktor (Create) wird die Anzahl erhöht, im Destruktor (Free) wieder herabgesetzt. Über die Klassenmethode Count (und natürlich über die globale Variable)
kann nun festgestellt werden, wie viele Instanzen es von TFlaeche gibt......"

Ich habe mich an diesem Beispiel orientiert, wohl falsch.
Hier wird mit dem Destructor TFlaeche.Count.Free gearbeitet.
Sollte man also immer "destructor ....Destroy" einsetzen?
Warum "override " was überschreibe ich da?

Der schöne Günther 29. Dez 2016 18:18

AW: liege ich richtig mit dem OOP-Versuch
 
Zitat:

Zitat von EdAdvokat (Beitrag 1357480)
Die Methoden, auf die die Propertys zugreifen (hier setLaenge, setBreite und GetFlaecheninhalt) lassen sich nicht in private oder protect verstecken.

Natürlich lassen sie sich das. Woran genau soll es scheitern?


Zitat:

Zitat von EdAdvokat (Beitrag 1357484)
Sollte man also immer "destructor ....Destroy" einsetzen?

Im Gegensatz zu anderen Sprachen hat der Destruktor in Delphi einen Namen. Man kann ihn nennen wie man will, sollte es aber wirklich nicht tun. Alle (bis auf dieses Tutorial) nennen ihn "destroy".

Der Grund weshalb man davon nicht abweichen sollte deckt sich mit deiner nächsten Frage

Zitat:

Zitat von EdAdvokat (Beitrag 1357484)
Warum "override " was überschreibe ich da?

Jede Klasse die du in Delphi definierst leitet sich implizit von der Ur-Klasse
Delphi-Quellcode:
TObject
ab. TObject hat einen Destruktor mit dem Namen "Destroy". Wenn ein Objekt deiner Klasse zerstört werden soll müssen alle Destruktoren die ganze Vererbungshierarchie hinauf abgearbeitet werden bis man bei TObject ankommt.

Der Destruktor von TObject ist "virtuell". Das bedeutet dass Unterklassen (z.B. deine) das Verhalten von "Destroy" abändern können. In deinem Fall zählt es die Klassenvariable "Anzahl" eins herunter damit man sieht dass es in der Welt nun eine Fläche weniger gibt. Doch hiernach muss im Destruktor noch "inherited" aufgerufen werden damit der Destruktor von TObject aufgerufen wird. Das fehlt in deinem Tutorial ebenfalls, das ist falsch.

Schau mal zu Themen wie "Vererbung" und "Polymorphie". Liest sich vielleicht beim ersten mal etwas wild, aber eigentlich ist es ganz einfach.


Als absolute Kurzfassung, so wäre das Tutorial korrekt:
Delphi-Quellcode:
interface

type
   TFläche = class // Man kann auch "TFläche = class(TObject)" schreiben
      public
         destructor Destroy(); override;
   end;
   

implementation

destructor TFläche.Destroy();
begin
   // Tue etwas
   inherited;
end;

TBx 29. Dez 2016 19:02

AW: liege ich richtig mit dem OOP-Versuch
 
Das Tutorial ist überarbeitungsbedürftig. Ich habe es auf meine ToDo-Liste gesetzt.

EdAdvokat 29. Dez 2016 21:05

AW: liege ich richtig mit dem OOP-Versuch
 
Danke für die Hinweise zum Destructor. Werde ich mir nochmals in Ruhe ansehen.
Warum sich bestimmte Methoden nicht verbergen lassen liegt im konkreten Fall daran, dass im Formular diese Methoden aufgerufen werden und wenn sie nicht
public sind, geht es nicht. Was könnte ich dagegen tun? Ich habe erkenntlich TForm in eine unit und TFlaecheninhalt in eine weitere unit gepackt. TForm-Methoden(Buttonclick...) greifen auf GetFlaecheninhalt beispielsweise zu :
Delphi-Quellcode:
procedure TForm1.BerechneFlClick(Sender: TObject);
begin
  Flaecheninh.setLaenge(strtoint(Laenge.text));
  Flaecheninh.setBreite(strtoint(Breite.text));
  LbFlaecheninhalt.Caption:=inttostr([B]Flaecheninh.GetFlaecheninhalt[/B]);
end;
Das ist mein kleines Problem. Oder muss ich damit leben?

TBx 29. Dez 2016 21:13

AW: liege ich richtig mit dem OOP-Versuch
 
Greif nicht auf die Methoden sondern auf das Property zu. Dafür ist das ja da.

Luckie 29. Dez 2016 21:55

AW: liege ich richtig mit dem OOP-Versuch
 
So hätte ich es gemacht:
Delphi-Quellcode:
Type
  TRechteck = class(TObject)
  private
    FLaenge: double;
   FBreite: double;
   FFlaeche: double;
   FUmfang: double;
    procedure BerechneFlaeche: Double;
    procedure BerechneUmfang: Double;
   procedure SetLaenge(laenge: double);
   procedure SetBreite(breite: double);
  public
   Property Laenge: Double write SetLaenge;
   Property Breite: Double write SetBreite;
    Property Flaeche: Double read FFlaeche;
    Property Umfang: Double read FUmfang;
  End;
 
  procedure TRechteck.SetLaenge(laenge: double);
  begin
    // ToDo:
   // wenn kleiner 0 -> Exception auslösen "Länge darf nicht kleiner 0 sein"
   // oder besser allgemeine Exception InvalideArgument
   laenge := FLaenge;
  end;
 
  procedure TRechteck.SetBreite(breite: double);
  begin
    // ToDo:
   // wenn kleiner 0 -> Exception auslösen "Breite darf nicht kleiner 0 sein"
   // oder besser allgemeine Exception InvalideArgument
   breite := FBreite;
  end;
 
  procedure TRechteck.BerechneFlaeche;
  begin
    FFlaeche := FLaenge * FBreite;
  end;
 
  procedure REechteck.BerechneUmfang;
  begin
    FUmfang := (2 * FLaenge) + (2 * FBreite);
  end;
 
///////////////////////////////////////////////////////////////////////////////

  procedure TForm1.BerechneFlClick(Sender: TObject);
  var
    TRechteck: Rechteck;
   Flaeche: double;
  begin
    Rechteck := TRechteck.Create;
   try
     // Eingaben validieren, wenn valid dann:
     Rechteck.Laenge := ...;
     Recheck.Breite := ...;
     Flaeche := Rechteck.Flaeche;
   finally
     Recheckt.Free;
   end;
  end;
Wozu der Zähler? Wenn jemand mehrere Flächen berechnen will, dann soll er das selbst implementieren. ;)

Aber du scheint das Prinzip der Objektorientierung noch nicht so ganz verstanden zu haben:
Delphi-Quellcode:
Type
  TFlaeche = class(TObject)
  private
  protected
    Function BerechneFlaeche: Double; virtual; abstract;
    Function BerechneUmfang: Double; virtual; abstract;
  public
Du willst anscheinen eine allgemeine Klasse schreiben, um Fläche und Umfang verschiedener geometrischer Körper zu berechnen. Und die Kind-Klassen sollen dann BerechneFlaeche und BerechneUmfang selbst implementieren. Idee gut. Aber Namensgebung schlecht. Die Klasse heißt TFlaeche. Warum berechnet eine Klasse TFlaeche den Umfang?

Delphi-Quellcode:
TGeometrischerKoerper = class(TObject)
Delphi-Quellcode:
TRechteck = class(TGeometrischerKoerper)
Delphi-Quellcode:
TDreieck = class(TGeometrischerKoerper)

Der schöne Günther 29. Dez 2016 22:09

AW: liege ich richtig mit dem OOP-Versuch
 
Zitat:

Zitat von Luckie (Beitrag 1357503)
Wozu der Zähler? Wenn jemand mehrere Flächen berechnen will, dann soll er das selbst implementieren.

Ich glaube das klassische Beispiel für Klassenvariablen. Klar kann man sich über den Sinn streiten, aber ich finde das als Beispiel ganz gut.

SProske 29. Dez 2016 22:14

AW: liege ich richtig mit dem OOP-Versuch
 
Zitat:

Zitat von Luckie (Beitrag 1357503)
Delphi-Quellcode:
  public
    Property Laenge: Double write SetLaenge;
    Property Breite: Double write SetBreite;
    Property Flaeche: Double read FFlaeche;

Wieso Länge und Breite als write only?
Und müsste die Property Flaeche nicht als read-procedure das BerechneFlaeche bekommen?

Zitat:

Zitat von Luckie (Beitrag 1357503)
Delphi-Quellcode:
 
  procedure TRechteck.SetLaenge(laenge: double);
  begin
    // ToDo:
   // wenn kleiner 0 -> Exception auslösen "Länge darf nicht kleiner 0 sein"
   // oder besser allgemeine Exception InvalideArgument
   laenge := FLaenge;
  end;

Sollte die Zuweisung nicht genau andersherum erfolgen?
Ist es eine gute Idee, den Parameter der Set-Procdure genau so zu nennen, wie die Property - irgendwann fängt man dann doch an zu überlegen, was von beiden eigentlich grad angesprochen wird.

EWeiss 29. Dez 2016 22:21

AW: liege ich richtig mit dem OOP-Versuch
 
Zitat:

Sollte die Zuweisung nicht genau andersherum erfolgen?
nein.. sorry da nur schreibzugriff
ja war quatsch von mir :)

gruss

Luckie 29. Dez 2016 22:22

AW: liege ich richtig mit dem OOP-Versuch
 
Ja natürlich andersherum. Sie heißen doch unterschiedlich.

Zitat:

Wieso Länge und Breite als write only?
OK, kann man drüber streiten, ob die Klasse auch die Eingabewerte wieder zurückgeben können soll. Weil die weiß ich ja, sonst hätte ich die Klasse ja gar nicht nutzen können.

Zitat:

Und müsste die Property Flaeche nicht als read-procedure das BerechneFlaeche bekommen?
Nein. Die private Prozedur zum Berechnen der Fläche legt das Ergebnis in FFlaeche ab.

SProske 29. Dez 2016 22:55

AW: liege ich richtig mit dem OOP-Versuch
 
Zitat:

Zitat von Luckie (Beitrag 1357507)
Ja natürlich andersherum. Sie heißen doch unterschiedlich.

Laenge und laenge sind für mich nicht unterschiedlich - zumindest mein Delphi ist in der Hinsicht nicht case-sensitiv.

Zitat:

Zitat von Luckie (Beitrag 1357507)
OK, kann man drüber streiten, ob die Klasse auch die Eingabewerte wieder zurückgeben können soll. Weil die weiß ich ja, sonst hätte ich die Klasse ja gar nicht nutzen können.

Naja, zwischen dem zuweisen der Werte und dem Zeitpunkt, wo ich gern wieder Zugriff drauf hätte, kann aber noch eine halbe bis ganze Weltreise liegen. Spätestens wenn ich mehrere Dreiecke in eine Liste stecke und diese nach der Länge sortieren will, wäre es schon cool auf die Länge auch zugreifen zu können.

Zitat:

Zitat von Luckie (Beitrag 1357507)
Nein. Die private Prozedur zum Berechnen der Fläche legt das Ergebnis in FFlaeche ab.

Und wo wird die aufgerufen? :>

Luckie 29. Dez 2016 23:33

AW: liege ich richtig mit dem OOP-Versuch
 
Unterschiedliche Namen:
Delphi-Quellcode:
 procedure TRechteck.SetLaenge(laenge: double);
  begin
    // ToDo:
   // wenn kleiner 0 -> Exception auslösen "Länge darf nicht kleiner 0 sein"
   // oder besser allgemeine Exception InvalideArgument
   FLaenge := laenge;
  end;
Der Parameter heißt laenge das Attribut FLaenge.

Delphi-Quellcode:
  procedure TRechteck.BerechneFlaeche;
  begin
    FFlaeche := FLaenge * FBreite;
  end;
 
 
  // Aufruf im Formular
  Flaeche := Rechteck.Flaeche;
Flaeche ist die private variable und Rechteck.Flaeche ist das Attribut Flaeche des Objektes Rechteck. Nix gleich.

Wie gesagt, man kann drüber streiten. Aber wenn du eine gescheite Datenhaltung machst, sollte es kein Problem sein an die Werte zu kommen. Wie gesagt, du musst sie ja selber wissen, wenn du die Klasse nutzen willst.


PS: War alles nur ins Notepad++ gehackt. Ich übernehme keine Gewähr für nichts. ;)

SProske 30. Dez 2016 07:44

AW: liege ich richtig mit dem OOP-Versuch
 
Zitat:

Zitat von Luckie (Beitrag 1357511)
Der Parameter heißt laenge das Attribut FLaenge.

Die Property heißt Laenge, der Parameter laenge - ich habe nie von dem Attribut gesprochen. Und da auch im Getter/Setter einer Property auf diese zugegriffen werden kann, sehe ich da schon Potential für Verwirrung.

Zitat:

Zitat von Luckie (Beitrag 1357511)
Delphi-Quellcode:
  procedure TRechteck.BerechneFlaeche;
  begin
    FFlaeche := FLaenge * FBreite;
  end;
 
 
  // Aufruf im Formular
  Flaeche := Rechteck.Flaeche;

Aber wo wird denn jemals die private Methode
Delphi-Quellcode:
procedure BerechneFlaeche: Double;
aufgerufen? In den Settern nicht, als Read-Methode der Property nicht und von außerhalb kommt man da nicht dran (okay, das ist gelogen - in der selben Unit schon - aber das wäre ja echt doof, wenn ich die Klasse nur innerhalb dieser Unit sinnvoll verwenden kann)

Zitat:

Zitat von Luckie (Beitrag 1357511)
Wie gesagt, man kann drüber streiten. Aber wenn du eine gescheite Datenhaltung machst, sollte es kein Problem sein an die Werte zu kommen. Wie gesagt, du musst sie ja selber wissen, wenn du die Klasse nutzen willst.

Im einfachst konstruierten Fall lese ich die Länge/Breite aus einer Datenbank aus und möchte sie dann, zusammen mit dem berechneten Fläche in einem Formular anzeigen - das ganze natürlich nicht nur für ein Rechteck, sondern für viele. Ich bräuchte dann also eine Rechteck-Klasse, die Breite und Länge als Read/Write-Property hält und eine, die Breite und Länge als Write-Only-Property hält und die Fläche berechnet?

Ich hätte das ganze dann doch eher folgend aufgebaut:

Delphi-Quellcode:
  TRechteck = class
  strict private
    FLaenge: Double;
    FBreite: Double;
    function GetFlaeche: Double;
    function GetUmfang: Double;
    procedure SetLaenge(const Value: Double);
    procedure SetBreite(const Value: Double);
  public
    property Laenge: Double read FLaenge write SetLaenge;
    property Breite: Double read FBreite write SetBreite;
    property Flaeche: Double read GetFlaeche;
    property Umfang: Double read GetUmfang;
  end

Luckie 30. Dez 2016 11:17

AW: liege ich richtig mit dem OOP-Versuch
 
Zitat:

Zitat von SProske (Beitrag 1357520)
Zitat:

Zitat von Luckie (Beitrag 1357511)
Delphi-Quellcode:
  procedure TRechteck.BerechneFlaeche;
  begin
    FFlaeche := FLaenge * FBreite;
  end;
 
 
  // Aufruf im Formular
  Flaeche := Rechteck.Flaeche;

Aber wo wird denn jemals die private Methode
Delphi-Quellcode:
procedure BerechneFlaeche: Double;
aufgerufen? In den Settern nicht, als Read-Methode der Property nicht und von außerhalb kommt man da nicht dran

Ah. Jetzt sehe ich, was du meinst. Öhm ja. Notepad++ eben. :angel2: Da müsste man noch was machen. Bisschen Voodoo oder so. :mrgreen:

EdAdvokat 30. Dez 2016 12:42

AW: liege ich richtig mit dem OOP-Versuch
 
Hallo, ich habe versucht, die Lösung von Sebastian (SProske) umzusetzen. Eine Unit Formuar und eine unit TFlaecheninhalt.
Ich komme einfach nicht mit den Werteeingaben weiter. Die Variablen L und B erhalten zwar die eingegebenen Werte, jedoch
Flaecheninh.Laenge und Flaecheninh.Breite bleiben auf 0 entspr. der Initialierung. Was mache ich da falsch und ist die
Umsetzung (natürlich ohne die variablen l und b im Formular) sonst richtig?


--------------------------------------------unit TForm1-----------------------------------------------------------
.....implementation

{$R *.dfm}

procedure TForm1.BerechneFlClick(Sender: TObject);
var L, B : double;
begin
try
l:= strtofloat(laenge.Text);
B:= strtofloat(Breite.Text);
Flaecheninh.Laenge:=L;
Flaecheninh.Breite:=B;
//Flaecheninh.Laenge:=strtofloat(Laenge.Text);
//Flaecheninh.Breite:=(strtofloat(Breite.text));
LbFlaecheninhalt.Caption:=floattostr(Flaecheninh.F laeche);
except
l:=(5);
b:=(4);
end;
end;
{------------------------------------------------------------------------------}
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
close;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
lbUmfang.Caption:=floattostr(Flaecheninh.Umfang);
end;

{------------------------------------------------------------------------------}
procedure TForm1.FormCreate(Sender: TObject);
begin
Flaecheninh := TFlaecheninh.create;
Flaecheninh.init;
end;
{------------------------------------------------------------------------------}
procedure TForm1.init;
begin
Laenge.Text:='0';
Breite.Text:='0';
lbFlaecheninhalt.Caption:='0';
end;
end.
------------------------------------------------------------------------------------------------------------------
--------------------------------------------unit TFlaecheninh-----------------------------------------------------

interface

type
TFlaecheninh = class
strict private
FLaenge: Double;
FBreite: Double;
function GetFlaeche: Double;
function GetUmfang: Double;
procedure SetLaenge(const Value: Double);
procedure SetBreite(const Value: Double);
public
constructor create;
destructor Destroy; override;
procedure init;
property Laenge: Double read FLaenge write SetLaenge;
property Breite: Double read FBreite write SetBreite;
property Flaeche: Double read GetFlaeche;
property Umfang: Double read GetUmfang;
end;

implementation

constructor TFlaecheninh.create;
begin
inherited create;
init;
end;
{------------------------------------------------------------------------------}
destructor TFlaecheninh.Destroy;
begin
inherited Destroy;
end;
{------------------------------------------------------------------------------}
procedure TFlaecheninh.init;
begin
FLaenge:=0.0;
Breite:=0.0;
end;
{------------------------------------------------------------------------------}
function TFlaecheninh.getFlaeche: double;
begin
result:=FLaenge * FBreite;
end;
{------------------------------------------------------------------------------}
procedure TFlaecheninh.setLaenge(const value:double);
begin
FLaenge:= Laenge;
end;
{------------------------------------------------------------------------------}
procedure TFlaecheninh.setBreite(const value:double);
begin
FBreite:= Breite;
end;
{------------------------------------------------------------------------------}
function TFlaecheninh.GetUmfang:double;
begin
result:=2*FLaenge+2*FBreite;
end;
end.

Der schöne Günther 30. Dez 2016 12:54

AW: liege ich richtig mit dem OOP-Versuch
 
Liste der Anhänge anzeigen (Anzahl: 1)
Tipp: Schau dir deine beiden Methoden
Delphi-Quellcode:
setLaenge
und
Delphi-Quellcode:
setBreite
noch einmal ganz genau an.


PS: Kannst du, wenn du Quelltext hier hochlädst, die bitte in einen "Delphi"-Block packen? Dann sind die viel einfacher zu lesen. Du hast, wenn du einen Beitrag schreibst, so ein Helm-Symbol über dem Text (siehe Bild). Dann ist das für uns deutlich einfacher zu lesen.

EdAdvokat 30. Dez 2016 15:16

AW: liege ich richtig mit dem OOP-Versuch
 
Danke habe den Fehler gefunden und es läuft. Zusammengefaßt: ich muss ich nicht unbedingt auf Methoden zugreifen, sondern kann
mit den propertys operieren; sie aufrufen bzw. ihnen Werte zuweisen und damit rechnen...Güligkeitsprüfungen vornehmen usw. Somit
können sie mit privat oder gar mit strict privat versteckt werden, um sie vor fremden Zugriff zu schützen.
Damit bin ich einen großen Schritt vorangekommen.
Die Problematik Destructor Destroy habe ich studiert und werde so wie von euch empfohlen verfahren, wenngleich ich neben dem bereits
zitierten Tutorial auch noch meinen Lieblingsautorenkoll. Michael Ebner und Christoph Klawun in GoTo Pascal mit Delphi 4 S. 398 zitieren
möchte: "Von TObject erben alle Objeke den Konstruktor Create sowie die Destruktoren Destroy und Free. (Der Destruktor Free untersucht zunächst,
ob überhaupt eine Instanz von dem Objekt erstellt wurde und ruft dann gegebenfalls Destroy auf. Im Zweifelsfall sollten Sie stets Free
verwenden.)
Doberenz und Kowalski in Borland Delphi 7 S 565 beschreiben die Destruktoren so wie ihr hier im Forum mit dem Aufruf von inherited Destroy.
Es gibt also durchaus unterschiedliche Betrachtungsweisen. Ich möchte keinesfalls eine Konstruktor/Destrukor-Diskussion befeuern. Ich
werde so verfahren wie hier empfohlen. Nochmals Danke für die Hinweise

SProske 30. Dez 2016 15:27

AW: liege ich richtig mit dem OOP-Versuch
 
Wenn wir bezüglich der Destruktoren mal in die Vorfahrenklasse TObject schauen, sieht das folgendermaßen aus:

Delphi-Quellcode:
  TObject = class
    procedure Free;
    destructor Destroy; virtual;

...

procedure TObject.Free;
begin
  if Self <> nil then
    Destroy;
end;

destructor TObject.Destroy;
begin
end;
Free ist also eine Methode, kein Destructor. Korrekt ist, dass man (so gut wie) immer
Delphi-Quellcode:
.Free
aufrufen sollte und nicht
Delphi-Quellcode:
.Destroy
Destroy ist als virtual deklariert und ist üblicherweise das, was man in seiner abgeleiteten Klasse (sofern nötig) überschreiben kann (wichtig: nicht das override vergessen!)

Der schöne Günther 30. Dez 2016 15:42

AW: liege ich richtig mit dem OOP-Versuch
 
Ich hoffe ich entführe das Thema nicht zum Schluss noch, aber die Properties würde ich persönlich nicht überbewerten - Das nur als Ratschlag von mir. So wie sie in Delphi geschaffen sind sehe ich nicht, was man durch sie gewinnt. Andere Sprachen kommen auch super ohne Properties aus.

Um ehrlich zu sein sehe ich nur Nachteile:
  • Eine Variable kann ich per Referenz übergeben. Das erlaubt Dinge wie
    Delphi-Quellcode:
    Inc(zähler);
    statt
    Delphi-Quellcode:
    zähler := zähler + 1;
    . Mit Properties geht das nicht.
  • Bei Properties sehe ich in der Code-Vervollständigung nicht ob ich sie auch beschreiben darf. Erst wenn es beim Kompilieren einen Fehler gibt.
  • Im Endeffekt nur zusätzlicher Tippaufwand.

EdAdvokat 30. Dez 2016 16:10

AW: liege ich richtig mit dem OOP-Versuch
 
nun doch "constructor/destroctor": Ich habe weitere Autoren befragt: Hans-Georg Schumann in Delphi für Kids spricht sich auf S 387 für free aus:
"Es könnte ja sein, dass beim Erzeugen eines Objekts ein Fehler auftritt. Und gerade die Methode Free sorgt dafür, dass auch ein nur zum Teil erzeugtes Objekt wieer ordnungsgemäß
freigegeben wird. Man sollte also zur Freigabe von Objekten nur Free benutzen.
Eigentlich könnten wir den von TObject geerbten Destrokor so übernehmen, wenn wir nicht auch ein paar eigene Aufräumungsarbeiten erledigen wollen. Dann könnte die Definition einer
eigenen Destroy-Methode zub. so aussehen:
destructor TMonster.Destroy;
begin
Form1.label....
Form1.label...
inherited Destroy;..
end;
Auf jeden Fall sollte zum Schluss der geerbte Destruktor aufgerufen werden. Die Muttermethode Free ruft nun den Destruktor auf - wenn er mit override gekennzeichnet wurde.
Dabei kommt (ganz am Ende) auch der Destruktor der Mutter zum Einsatz."

Autorenkoll. Delphi 3 im Team: S 135 ff wie gehabt

constructor create(AOwner: TComponent); override;
destructor Destroy; override;

...die Methode Free der Klasse T... dient dabei der nötigen Freigabe des Speichers. Auch hier rufen wir wieder den Destruktor der Elternklasse auf.

Bohne/Lang in GoTo Delphi 4 S 282: " Hier wir für jede Kompnente der Destruktor Destroy aufgerufen, der den von der Komponente werdendeten Speicher freigigt. Normalerweise
sollte Destroy nicht direkt aufgerufen werden, satt dessen sollte die sichere Funktion Free aufgerufen werden, die selbst Destroy aufruft...."

Elmar Warken in Delphi 4 S. 193 ff meint..."Empfehlenswerter ist es jedoch statt dessen die Methode Free aufzurufen...."

So nun genug Literatur. Es gibt also durchaus unterschiedliche Herangehensweisen, die alle nicht falsch sein müssen. Die einen sind gründlicher doch auch die anderen kommen
zum Ziel.
Sollte sich jemand finden, das Tutorial zu überarbeiten, könnte ggf. auf diese Literatur zurückgegriffen werden.

Jumpy 30. Dez 2016 16:20

AW: liege ich richtig mit dem OOP-Versuch
 
Nur mal zur Klarstellung:

Wenn du in deiner Klasse einen eigenen Destruktor brauchst, weil Klassenspezifische Dinge "abgeräumt" werden müssen, dann überschreibe die Methode / den Destructor "Destroy".
Das ist das eine!


Wenn du eine Klasse erzeugt und damit was gemacht hast, dann musst du sie am Ende wieder frei geben. Da sollte dann die (von TObject durchgeerbte) Methode "Free" aufgerufen werden (und nicht selber Destroy aufrufen!). Die Methode Free ruft dann intern (dein) Destroy auf und wenn in deiner Destroy-Methode ein inherited steht (was so sein sollte), dann auch das Destroy der Vorfahren Klasse.
Das ist das andere!

Nichts anderes steht letztlich in den von dir genannten Zitaten.

TBx 30. Dez 2016 16:38

AW: liege ich richtig mit dem OOP-Versuch
 
Wie ich bereits geschrieben habe, ist die Überarbeitung des Tutorials auf meiner ToDo-Liste.

Zusammenfassend kann man sagen, dass man Objekte über
Delphi-Quellcode:
.Free
freigeben sollte.
Leitet man eine Klasse ab, so ist der Destructor
Delphi-Quellcode:
.Destroy
zu überschreiben, wenn bei der Zerstörung des Objektes weitere Aufräumarbeiten notwendig sind. Wie schon genannt wurde, ist der Destructor mit
Delphi-Quellcode:
destructor Destroy; override;
zu überschreiben, da sonst die Polymorphie untergraben würde und dadurch das von TObject geerbte Free nicht den korrekten Destructor aufrufen würde.
Eine ganz schlechte Idee ist es, das Free neu zu implementieren. Überschreiben kann man es nicht, da es in der Basisklasse nicht virtual ist. Aufräumarbeiten gehören per Definition in den Destructor. Free ist nur eine Methode, die nicht jeder nutzen muss. Es ist durchaus legitim, Destroy aufzurufen, wenn man sicher ist, dass es das Objekt noch gibt. Hat man nun die Aufräumarbeiten in einem überdeckenden
Delphi-Quellcode:
procedure Free; reintroduce;
implementiert, werden diese nicht ausgeführt.
Etwas anderes sagen die zitierten Autoren nicht.
Eine Aussage ist allerdings unsinnig,
Delphi-Quellcode:
Free
kann nicht vollständig erzeugte Objekte nicht besser oder schlechter freigeben als
Delphi-Quellcode:
Destroy
. Die Methode Free prüft nur, ob die Variable, über die die Methode aufgerufen wird auf eine gültige Instanz von TObject weist und ruft dann Destroy auf.

EdAdvokat 30. Dez 2016 16:59

AW: liege ich richtig mit dem OOP-Versuch
 
Liste der Anhänge anzeigen (Anzahl: 1)
nur zur Vollständigkeit das kleine Testprog. Flaecheninhalt gemäß den Ratschlägen. Nochmals Danke für die Unterstützung

TBx 30. Dez 2016 23:56

AW: liege ich richtig mit dem OOP-Versuch
 
So, ich habe die irritierende Benamselung des Destruktors im Tutorial korrigiert.

Luckie 31. Dez 2016 00:41

AW: liege ich richtig mit dem OOP-Versuch
 
Kurz. Den Destructor muss man nur überschreiben, wenn man selber in der Klasse Objekte erstellt, die freigegeben werden müssen. Und dann ruft man ganz zu Anfang im Code vom Destructor inherited auf, um den Destructor der Elternklasse aufzurufen und dann gibt man seinen Kram frei.

TBx 31. Dez 2016 07:27

AW: liege ich richtig mit dem OOP-Versuch
 
Hier muss ich Dir widersprechen. Es geht nicht nur um weitere Objekte, die freigegeben werden müssen, sondern um alle Arbeiten, die bei Zerstörung des Objektes ausgeführt werden müssen. Und im Destructor wird das
Delphi-Quellcode:
inherited
als Letztes aufgerufen.

p80286 31. Dez 2016 09:00

AW: liege ich richtig mit dem OOP-Versuch
 
Schau mal auf die Uhrzeit, da kann man Constructor und Destructor schon mal verwechseln was die Reihenfolge des inherited angeht.

Gruß
K-H

Delphi-Laie 31. Dez 2016 14:55

AW: liege ich richtig mit dem OOP-Versuch
 
Zitat:

Zitat von Luckie (Beitrag 1357613)
Kurz. Den Destructor muss man nur überschreiben, wenn man selber in der Klasse Objekte erstellt, die freigegeben werden müssen. Und dann ruft man ganz zu Anfang im Code vom Destructor inherited auf, um den Destructor der Elternklasse aufzurufen und dann gibt man seinen Kram frei.

An welcher Stelle im Destruktor dieses
Delphi-Quellcode:
inherited {destroy}
korrekt placiert ist, weiß ich bis heute nicht.

Ich tat es bisher am Ende des Destroys, weil in der Delphi-Hilfe (Delphi 3) folgendes Codebeispiel enthalten ist:

Zitat:

Im folgenden Beispiel sehen Sie, wie der im vorhergehenden Abschnitt für die Klasse TShape deklarierte Destruktor implementiert werden kann:
Delphi-Quellcode:
destructor TShape.Destroy;
begin
 FBrush.Free;
 FPen.Free;
 inherited Destroy;
end;
Die letzte Aktion eines Destruktors ist normalerweise der Aufruf des geerbten Destruktors, mit dem die geerbten Felder des Objekts freigegeben werden.
Was stimmt denn nun?

nahpets 31. Dez 2016 15:29

AW: liege ich richtig mit dem OOP-Versuch
 
Ich formuliere es mal so:

Bevor ich mich wegwerfe, schmeiße ich alles weg, was ich verbrochen habe, es könnte sonst sein, dass es mich beim Wegwerfen meiner Schandtaten nicht mehr gibt, obwohl sie zu diesem Zeitpunkt noch auf meine Existenz angewiesen sein könnten.

Daher folge ich dem Beispiel aus der Delphi-Hilfe.

Ein Objekt wird immer erst dann weggeräumt, wenn alles, was von ihm abhängig sein könnte, ordnungsgemäß aufgeräumt wurde.

Delphi-Laie 31. Dez 2016 19:26

AW: liege ich richtig mit dem OOP-Versuch
 
Danke, das leuchtet ein.

Nichtdestoweniger interessiere ich mich natürlich auch für Luckies Antwort dazu (vermutlich und hoffentlich im neuen Jahr?!).

Luckie 1. Jan 2017 02:16

AW: liege ich richtig mit dem OOP-Versuch
 
Ja. Natürlich. Im Konstruktor zu erst, im Destruktor zuletzt. So rum ist es natürlich richtig.

Luckie 1. Jan 2017 02:20

AW: liege ich richtig mit dem OOP-Versuch
 
Zitat:

Zitat von p80286 (Beitrag 1357616)
Schau mal auf die Uhrzeit, da kann man Constructor und Destructor schon mal verwechseln was die Reihenfolge des inherited angeht.

Hehe. Danke für die Ausrede. :mrgreen:

EdAdvokat 1. Jan 2017 11:51

AW: liege ich richtig mit dem OOP-Versuch
 
Hallo nochmals eine Frage zum leidigen Thema Destructor:
habe folgenden Code geschrieben im type Tirgendwas:

Delphi-Quellcode:
public
  constructor create;
  destructor destroy; override;
  procedure init;

dann den Destructor aufgerufen mit:

destructor TKredit.destroy;
begin
    inherited destroy;
end;
Ich erhalte dann folgenden Hinweis:
[dcc32 Hinweis] uUnit1.pas(19): H2365 Schreibweise der Überschreiben-Methode TKredit.destroy muss exakt ihrem Vorfahren TObject.Destroy entsprechen
Was muss ich anders machen?

DeddyH 1. Jan 2017 11:58

AW: liege ich richtig mit dem OOP-Versuch
 
Destroy mit großem D schreiben. Das liegt einfach daran, dass Delphi und C++ dieselben Bibliotheken nutzen und C++ case-sensitive ist.

EdAdvokat 1. Jan 2017 19:45

AW: liege ich richtig mit dem OOP-Versuch
 
Danke, irre, darauf sollte einer kommen!!! Es gibt nun keinen Hinweis mehr!!!

himitsu 1. Jan 2017 22:59

AW: liege ich richtig mit dem OOP-Versuch
 
Zum Glück hat jetzt keiner darauf hingewiesen, dass die Lösung sogar direkt in dem Compiler-Hinweis genannt wurde.


Alle Zeitangaben in WEZ +1. Es ist jetzt 00:40 Uhr.
Seite 1 von 2  1 2      

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