![]() |
Re: unsichtbare Klassen
1. Entweder einfach im Implementation Teil deklararieren.
2. Oder als private Sub-Klasse definieren 3. Auslagern und diese Unit Unit-privat im Implementation Teil einbinden Diese Möglichkeiten sehe ich ... |
Re: unsichtbare Klassen
Zitat:
Zitat:
Ich zeig am besten mal den Code, wie er bisher war:
Delphi-Quellcode:
Wie genau müsste ich den dann umschreiben?
unit FIFO;
interface type TNode = class private FNext: TNode; FObject: TObject; constructor Create(AObject: TObject); destructor Destroy; override; end; TFIFO = class private FFront: TNode; FRear: TNode; public constructor Create; destructor Destroy; override; procedure Clear; function Dequeue: TObject; procedure Enqueue(AObject: TObject); end; implementation constructor TNode.Create(AObject: TObject); begin inherited Create; FObject := AObject; FNext := nil; end; destructor TNode.Destroy; begin FObject.Free; inherited; end; constructor TFIFO.Create; begin inherited; FFront := nil; FRear := nil; end; destructor TFIFO.Destroy; begin Clear; inherited; end; procedure TFIFO.Clear; begin while FFront <> nil do Dequeue.Free; end; function TFIFO.Dequeue; begin if FFront <> nil then begin Result := FFront.FObject; FFront := FFront.FNext; end else Result := nil; end; procedure TFIFO.Enqueue(AObject: TObject); begin if FRear <> nil then begin FRear.FNext := TNode.Create(AObject); FRear := FRear.FNext; end else begin FRear := TNode.Create(AObject); FFront := FRear; end; end; end. |
Re: unsichtbare Klassen
Zitat:
Zitat:
Delphi-Quellcode:
/EDIT: Programmierst du wirklich mit TABs? Das gehört meiner Meinung nach verboten...
unit FIFO;
interface type TFIFO = class private Type TNode = class private FNext: TNode; FObject: TObject; public constructor Create(AObject: TObject); destructor Destroy; override; end; private FFront: TNode; FRear: TNode; public constructor Create; destructor Destroy; override; procedure Clear; function Dequeue: TObject; procedure Enqueue(AObject: TObject); end; implementation constructor TNode.Create(AObject: TObject); begin inherited Create; FObject := AObject; FNext := nil; end; destructor TNode.Destroy; begin FObject.Free; inherited; end; constructor TFIFO.Create; begin inherited; FFront := nil; FRear := nil; end; destructor TFIFO.Destroy; begin Clear; inherited; end; procedure TFIFO.Clear; begin while FFront <> nil do Dequeue.Free; end; function TFIFO.Dequeue; begin if FFront <> nil then begin Result := FFront.FObject; FFront := FFront.FNext; end else Result := nil; end; procedure TFIFO.Enqueue(AObject: TObject); begin if FRear <> nil then begin FRear.FNext := TNode.Create(AObject); FRear := FRear.FNext; end else begin FRear := TNode.Create(AObject); FFront := FRear; end; end; end. /EDIT2: Den Constructor und Desctructor kannst du nicht einfach in eine geringere Sichtbarkeit verschieben... |
Re: unsichtbare Klassen
Zitat:
Zitat:
![]() Aber falls du einen guten Grund dagegen hast, würd ich den gerne hören (falls es hier zu OT wird, dann evtl per PM oder im verlinkten Thread. |
Re: unsichtbare Klassen
Zitat:
Zitat:
![]() |
Re: unsichtbare Klassen
Zitat:
|
Re: unsichtbare Klassen
Zitat:
Zitat:
|
Re: unsichtbare Klassen
Ja, Tabs sind echt schön. :mrgreen: Trotzdem Zwischenfrage : wozu konkret dient das zweimalige "override" ? :shock:
|
Re: unsichtbare Klassen
Zitat:
greetz Mike |
Re: unsichtbare Klassen
Ja, was soll denn überschrieben werden ? :shock: Da ist doch nichts vorher.
|
Re: unsichtbare Klassen
Zitat:
Delphi-Quellcode:
;)
destructor Destroy; virtual;
greetz Mike |
Re: unsichtbare Klassen
Jason, ich meine das hier :
Delphi-Quellcode:
Delphi wird das schon schlucken. Nur, was soll das ?
type
TFIFO = class destructor Destroy; override; end; |
Re: unsichtbare Klassen
Zitat:
greetz Mike |
Re: unsichtbare Klassen
Dann zeige mal wo der sein soll. :zwinker: Mittlerweile auch gemerkt, dass da was nicht stimmt ? :mrgreen: Oder, was habe ich in Object-Pascal wann verpasst ?
|
Re: unsichtbare Klassen
In TObject? :gruebel:
|
Re: unsichtbare Klassen
Zitat:
![]()
Delphi-Quellcode:
greetz
Type
TObject = Class Public destructor destroy;virtual; Mike |
Re: unsichtbare Klassen
Blinde Kuh ? Ja wär doch gut. :lol:
Nächster Versuch :
Delphi-Quellcode:
Erkläre mir mal bitte, warum da ein Destructor einer leeren Class TFIFO (also ohne Vorfahr) per override überschrieben werden muss. Mehr will ich gar nicht wissen. Wer soll mit dem "override" denn überhaupt angesprochen werden ? :shock:
type
TFIFO = class private Type TNode = class private FNext: TNode; FObject: TObject; public constructor Create(AObject: TObject); destructor Destroy; override; end; |
Re: unsichtbare Klassen
Zitat:
gute Nacht Mike |
Re: unsichtbare Klassen
Ich hänge dazu nochmal schnell die Meinung der OH an, welches dies auch ausdrücklich beschreibt:
Zitat:
/EDIT: Wenn du das Override nicht angibst, dann erhälst du zum einen einen Hinweis/Warnung vom Compiler und zum anderen wird dein Destructor nie ausgeführt. |
Re: unsichtbare Klassen
Zitat:
Er wird nur dann nicht ausgeführt, wenn zum Freigeben von TMeinObjekt ein MeinObjekt.Free benutzt wird. Free ist von TObject geerbt und ruft eben nur den Destruktor destroy von TObject auf, wenn dessen Virtualität nicht Rechnung getragen wird in den abgeleiteten Klassen. Durch ein override würde MeinObjekt.Free den Destructor destroy von TMeinObjekt aufrufen. Daher sollten Destruktoren in der Regel immer ihren Ahnen per override überschreiben. Eben weil sonst ein Free den eigenen Destruktor nicht "erreichen" würde, sondern in TObject "hängenbleiben" würde. Nun kann man aber locker auch den Destruktor direkt aufrufen, um ein TMeinObjekt freizugeben: MeinObjekt.Destroy. In diesem Fall würde der Destruktor von TMeinObjekt immer aufgerufen werden, ohne jegliche Relevanz dessen, ob er seinen Ahnen in TObject überschreibt oder nicht. Allerdings ist diese Art des Destruktoren-Aufrufs nicht sooo sonderlich empfehlenswert, weil zuvor überprüft werden muss, ob MeinObjekt nicht schon nil ist. Nichts anderes macht nämlich TObject.Free. :-) |
Re: unsichtbare Klassen
gleiches gilt neben dem Aufruf von xxx.Free; auch für die Verwendung/Aufruf von FreeAndNil(xxx);
|
Re: unsichtbare Klassen
Vorab : der Thread hier wurde vom Fragesteller woanders mittlerweile als OT gebrandmarkt. :mrgreen:
Nichtsdestotrotz : das ganze sieht doch so aus :
Delphi-Quellcode:
Wie Muetze richtig aus der Hilfe zitiert hat, ist es dasselbe einfach "class" zu schreiben oder eben "class (TObject)". Also ist das ein Nachfahre von TObject. Aber nur pro forma. Warum soll jetzt da nochmals ein leerer Destructor per override auch noch überschrieben werden ? :shock: Vermute sogar, dass der Delphi-Linker das automatisch entfernt. Naheliegend wäre auch, das die vereinfachte Schreibweise Class erlaubt wird, damit keiner auf die Idee kommt, so was überflüssigerweise zu machen. :-D
destructor TObject.Destroy;
begin end; |
Re: unsichtbare Klassen
Zitat:
Du hast eine beliebige Klasse, bspw. TMeineKlasse. Im Konstruktor dieser Klasse wird bspw. Speicher allociert, oder irgendetwas instanziert, das im Destruktor wieder freigegeben werden muss. Nun gibts 2 Moeglichkeiten:
Delphi-Quellcode:
Du behauptest, zweiteres mache keinen Sinn. Analysieren wir mal, was die unterschiede sind:
//1.
destructor Destroy(); //2. destructor Destroy(); override; Wenn eine Variable als TMeineKlasse deklariert ist, macht es keinen Unterschied. Wenn aber eine Variable nicht als TMeineKlasse, sondern als Vorfahre (bspw. TObject) deklariert ist, aber als TMeineKlasse instanziert wurde, muss auch der Destruktor von TMeineKlasse aufgerufen werden. Passiert das aber? Nein.
Delphi-Quellcode:
Gibt ein schoenes Speicherleck. mit dem ueberschreiben des Destruktors passiert das nicht.
TMeineKlasse = class
constructor Create(); destructor Destroy(); speicher: Pointer; end; //... constructor TMeineKlasse.Create(); begin speicher = allocmem(100); end; destructor TMeineKlasse.Create(); begin freemem(speicher); end; procedure ProbiersAus(); var x: TObject; begin x := TMeineKlasse.Create(); x.Free(); //Es wird TObject.Free aufgerufen! end; greetz Mike |
Re: unsichtbare Klassen
Zitat:
Den Aufruf von Inherited würde ich aber auch nicht weglassen, da sonst bei einer Änderung des Inhalts des Destructors in TObject seitens Borlands nicht Rechnung getragen würde. /EDIT: roter Rahmen verzweifelt gesucht... |
Re: unsichtbare Klassen
Zitat:
Zitat:
|
Re: unsichtbare Klassen
@JasonDX
Zitat:
Delphi-Quellcode:
Dieser "Umweg" über das Free, welches nur im Urahnen TObject existiert, macht beim Aufruf des Destructors den kleinen Unterschied.
var
x: TMeineKlasse; |
Re: unsichtbare Klassen
Ich liebe Grundsatzdiskussionen, aber wir sind anscheinend noch lange nicht fertig. :mrgreen:
Erkläre mir mal bitte einer, was ich in diesem Fall mit dem overrride soll :
Delphi-Quellcode:
Das Programm zeigt 2-mal '123' an, wie erwartet. Geht man dann noch hin und macht das IMHO überflüssige Destroy und alles was damit zusammenhängt weg, dann tut es das auch.
Unit Unit2;
interface implementation uses Dialogs, SysUtils; type TMeineKlasse = class private i : integer; protected constructor Create; destructor Destroy; end; var MeineKlasse : TMeineKlasse; constructor TMeineKlasse.Create; begin i := 123; end; destructor TMeineKlasse.Destroy; begin showmessage ('Destroy '+IntToStr (i)); end; begin MeineKlasse := TMeineKlasse.Create; showmessage (IntToStr (MeineKlasse.i)); MeineKlasse.Destroy; end. Wer hat ein Gegenbeispiel ? Es darf aber wirklich nur als Class deklariert sein. Das TObject-Free von Jason ist auch gemogelt, es geht um Destroy. Free hat eingebaute Funktionalität. Das ist schon was anderes. |
Re: unsichtbare Klassen
Moin Hansa,
das ist ungefähr das, was JasonDX meinte. Der direkte Aufruf von Destroy in deinem Beispiel verhält sich so, wie du sagst. Was anderes hatte JasonDX auch nicht behauptet. Jetzt gibt es aber Fälle, wo (jetzt bezogen auf dein Beispiel) nicht
Delphi-Quellcode:
sondern
var MeineKlasse : TMeineKlasse;
Delphi-Quellcode:
gesetzt wird, und dennoch wie folgt created wird:
var MeineKlasse : TObject;
Delphi-Quellcode:
Jetzt erreicht dein Code den Destructor von TMeineKlasse nicht mehr.
MeineKlasse := TMeineKlasse.Create;
Zitat:
Wer ruft denn bitteschön Destroy direkt auf? Für dein Beispiel war es jetzt ja ganz gut und schön und anschaulich. Aber in der Praxis sollte und wird die Methode Free oder die Procedure FreeAndNil() benutzt. Und genau dann ist override in jedem Fall erforderlich. |
Re: unsichtbare Klassen
Zitat:
Delphi-Quellcode:
Kein Destroy mehr, auch kein override und es geht immer noch. Ich will lediglich wissen, was einige hier vorhaben mit override etc. Und sieh mal einer an, was steht denn da :
Unit Unit2;
interface implementation uses Dialogs, SysUtils; type TMeineKlasse = class private i : integer; constructor Create; end; var MeineKlasse : TMeineKlasse; constructor TMeineKlasse.Create; begin i := 123; end; begin MeineKlasse := TMeineKlasse.Create; showmessage (IntToStr (MeineKlasse.i)); end.
Delphi-Quellcode:
:shock: Da ist ja sogar das Create eventuell fällig. :mrgreen:
constructor TObject.Create;
begin end; Edit : es sei angemerkt, dass nicht mal inherited verwendet wird ! Normalerweise wäre das schon nötig. |
Re: unsichtbare Klassen
Zitat:
Du willst wissen, wie es sich mit virtuellen Methoden und override verhält? Fein. Steht hier in diesem Thread jetzt doppelt und dreifach. Lies in. ;-) In der Hilfe steht das ebenfalls recht ansprechend beschrieben. Die Geschichte mit Free ist zudem ein sehr schönes Beispiel für die Implemetierung in einer Klassenhierarchie. Kann man herrlich ausprobieren und (auch wenn's schwer ist) verstehen. Tue es, oder lass es. Ich habe keinen Bock, dir zu erklären, dass das Gras grün ist. Wenn du jede Klasse ausschließlich so instantiierst wie sie deklariert ist und wenn du eigenen Klassen immer einen statischen Destruktor verpasst und diesen immer direkt mit xxx.Destroy aufrufst, dann wirst du möglicherweise immer zurecht kommen. Oder um auf das Gras zurückzukommen: Wenn du dein ganzen Grundstück zupflasterst, ist das in Ordnung. Die Rasenflächen der Anderen sind aber immer noch grün - ganz egal, ob du das akzeptierst oder nicht. :spin2: |
Re: unsichtbare Klassen
So, jetzt nochmal gaaaaaanz laaaaangsam, und schoooooeeen detailliert:
Regeln:
So, und nun was passiert: Gegeben sei folgende Klasse
Delphi-Quellcode:
und diese 2 Variablen:
TMeineKlasse = class(TObject)
constructor Create(); destructor Destroy(); //override; end;
Delphi-Quellcode:
welche beide mit
var
meineKlasseObject: TObject; meineKlasse: TMeineKlasse;
Delphi-Quellcode:
initialisiert werden.
TMeineKlasse.Create();
Beobachten wir nun beide moeglichkeiten, und rufen bei beiden Instanzen die Destroy-Methode auf (weil du ja meinst, mit Free waere das ganze gezinkt :roll: ) Zuerst ohne override:
Delphi-Quellcode:
Der Kompiler guckt: meineKlasse ist als TMeineKlasse deklariert. Davon soll nun die Methode "Destroy" aufgerufen werden. Ja, die is da deklariert, und dementsprechend wird auch TMeineKlasse.Destroy aufgerufen.
meineKlasse.Destroy();
nun
Delphi-Quellcode:
Der Kompiler guckt: meineKlasseObject ist als TObject deklariert. Davon soll nun die Methode "Destroy" aufgerufen werden. Ja, TObject.Destroy() gibts, und ist als virtual derklariert. Also guckt er in der VMT nach: Durch das instanzieren mit TMeineKlasse.Create() wird auf die VMT von TMeineKlasse verwiesen. Dort findet sich allerdings immernoch nur der Eintrag auf TObject.Destroy(), also wird TObject.Destroy aufgerufen. -> Speicherleck (siehe mein vorheriger Beitrag).
meineKlasseObject.Destroy();
so, und nun das ganze mit override: beim aufruf von
Delphi-Quellcode:
veraendert sich nichts. Der Kompiler sieht immernoch die Methode TMeineKlasse.Destroy() (weil meineKlasse als TMeineKlasse deklariert ist), und ruft dementsprechend auch TMeineKlasse.Destroy() auf.
meineKlasse.Destroy
Der Unterschied kommt bei
Delphi-Quellcode:
Hier guckt naemlich der Kompiler: Es soll die Methode Destroy von TObject aufgerufen werden. Diese Methode ist als virtual deklariert. Also gucken wir in der VMT nach. Weil diese aufgrund des Instanzierens durch TMeineKlasse.Create auf die VMT von TMeineKlasse geleitet wird, und der Eintrag von Destroy durch das override auf TMeineKlasse.Destroy() ueberschrieben wird, ruft der Kompiler auch TMeineKlasse.Destroy() auf -> Kein Speicherleck.
meineKlasseObject.Destroy();
[Edit]Absatz entfernt. Er kam etwas persoenlich angreifender rueber als erhofft. Sorry, Klaerung per PN erfolgt.[/Edit] greetz Mike |
Re: unsichtbare Klassen
Zitat:
Zitat:
|
Re: unsichtbare Klassen
Zitat:
Zitat:
Akzeptiere das einfach. Wenn dus verstehen willst, lies dir den Thread nochmal durch. Wenn dus dann immernoch nicht verstanden hast, dann beachte den letzten Absatz meines vorherigen Beitrages. greetz Mike |
Re: unsichtbare Klassen
Könntet ihr bitte zurück zum Thema kommen (unsichtbare Klassen)?! Wenn ihr über Grundlagen der Objectorientierten Programmierung diskutieren wollt macht bitte einen eigenen Thread auf.
|
Re: unsichtbare Klassen
Zitat:
Kannst du den Thread aufteilen und diesen Teil mit Titel "Warum virtuelle Destructoren?" (oder so ähnlich) abhängen? Wäre vielleicht ganz nützlich, dieses Offtopic hier zu Ontopic woanders zu machen. ;-) Danke. |
Re: unsichtbare Klassen
Zitat:
|
Re: Warum virtuelle Destructoren?
Öhm Jungs, ich glaube jeder von euch hat schon mindestens einmal in einem Thread mitgewirkt in dem versucht wurde dem großen H zu erklären was abstrakte Methoden sind, oder Interfaces, oder was-weiß-ich-nicht-alles.
Und ich glaube jeder von euch hat ab irgendeinem Punkt in diesen Threads aufgegeben. (Zwangsläufig, das große H weiß immer noch wofür abstrakte Methoden gut sind :zwinker: ) Warum gebt ihr nicht gleich von vornherein auf und antwortet einfach gar nicht mehr auf seine Fragen, die von seiner überheblicher Unwissenheit strotzen, dass sich mir die Eingeweide verknoten? Man kann sich auch auf angenehmere Art Magengeschwüre zuziehen... :zwinker: |
Re: Warum virtuelle Destructoren?
Ich gebe noch nich auf, sondern fange jetzt erst an. :mrgreen: Ich habe mich sehr über die Fortsetzung meines Threads
![]() @Hansa: Starte bitte eine Delphi-Anwendung mit folgendem Code:
Delphi-Quellcode:
unit Unit1;
interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) procedure FormCreate(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; TMeineKlasse = class public destructor Destroy; end; var Form1: TForm1; implementation {$R *.DFM} destructor TMeineKlasse.Destroy; begin ShowMessage('Hey, Destruktor von TMeineKlasse wurde aufgerufen. ' + 'Wenn diese Meldung gleich 2mal hintereinander kommt, ist es in der ' + 'Tat absolut unnötig, Destruktoren mit override zu kennzeichnen.'); end; procedure TForm1.FormCreate(Sender: TObject); var MeineKlasse: TMeineKlasse; MeineKlasseObjekt: TObject; begin MeineKlasse := TMeineKlasse.Create; MeineKlasse.Free; MeineKlasseObjekt := TMeineKlasse.Create; MeineKlasseObjekt.Destroy; ShowMessage('Wenn dies die erste oder zweite ausgegebene Meldung ' + 'nach Programmstart ist, ist dies neben der unschönen Warnung, ' + 'mit der mich der Compiler gerade die ganze Zeit nervt, ein weiterer ' + 'Grund, Destruktoren zu overriden.'); end; end. |
Re: Warum virtuelle Destructoren?
Ich gebe hier mal ein paar goldene Regeln vor.
An die Skeptiker hier: Ihr müsst das einfach als gegeben annehmen. Es wurde von Borland so designt. Ich kann nur empfehlen, diese Regeln immer zu beachten. :warn: Ausdrucken, lesen und an den Monitor kleben... 1.) es gibt nur EINEN Destruktor! (Es kann nur einen geben) Grund: ein Objekt kann auch ohne dass der Programmierer explizit der Destruktor aufgerufen hat, sterben. Man denke hier nur an die Referenzzählung über COM Interfaces. 2.) der Destruktor muss immer Destroy heisen ! Pro Klasse darf man einen Destruktor namens Destroy deklarieren 3.) dagegen kann es beliebig viele Konstruktoren geben 4.) dieser eine Destruktor ist von Hause aus virtuell (und das hat seine Gründe und Vorteile, wie oben schon vor Einigen erklärt wurde) Zusammen mit 1) bedeutet das, dass man keine neue Arten von Destruktoren einführen kann 5.) Innerhalb des Destruktors muss man grundsätzlich immer inherited als letzte Anweisung aufrufen.
Delphi-Quellcode:
Jede Klasse räumt seine selbst belegten Resourcen ab und muss dann den Destruktor der ererbten Klasse aufrufen. TObject.Destroy gibt dann der Speicher für das Objekt frei.
destructor TMeinObjekt.destroy;
begin FInneresObj.Free; inherited; // <= wer das vergisst, den holt der Speicherfresser !!! end; 6.) Destroy wird grundsätzlich niemals direkt aufgerufen; es muss Free benutzt werden
Delphi-Quellcode:
7.) der Destruktor muss mit Override deklariert werden; alles andere wäre falsch (dies erklärt sich aus 1.) und 4.))
FMeinObjekt.Destroy; // <= so ist der Ärger vorprogrammiert (FMeinObjekt könnte nil sein) !!
FMeinObjekt.Free; // das ist ok FreeAndNil(FMeinObjekt); // auch erlaubt aber mehr Overhead // möglich (aber ziemlich blöd, da es ja Free gibt) wäre auch if Assigned(FMeinObjekt) then FMeinObjekt.Destroy;
Delphi-Quellcode:
TMeinObjekt = class(TPersistent)
public destructor Destroy;override; end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:26 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz