AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Algorithmen, Datenstrukturen und Klassendesign Delphi Warum virtual / override bei destructor / constructor?

Warum virtual / override bei destructor / constructor?

Ein Thema von HJay · begonnen am 22. Mai 2017 · letzter Beitrag vom 14. Jan 2023
Antwort Antwort
Seite 2 von 3     12 3   
HJay

Registriert seit: 7. Dez 2009
172 Beiträge
 
Delphi XE7 Enterprise
 
#11

AW: Warum virtual / override bei destructor / constructor?

  Alt 22. Mai 2017, 14:14
Mir scheint, da sind noch ein paar Wissenslücken bzgl OOP/Polymorphie verhanden, die das Verständnis etwas erschweren.
Korrekt. Nun, ich versuche ja gerade, diese Lücken zu füllen... ich programmiere schon lange in OOP, aber gewisse Details leuchten mir einfach partout nicht ein, egal wie viel ich darüber lese.

Delphi-Quellcode:
 TMyParent = class(TObject)
  public
    procedure Something;
  end;

  TMyChild = class(TMyParent)
  public
    procedure Something;
  end;

implementation

{ TMyParent }

procedure TMyParent.Something;
begin
  Form1.Memo1.Lines.Add('Parent');
end;

{ TMyChild }

procedure TMyChild.Something;
begin
  inherited Something;
  Form1.Memo1.Lines.Add('Child');
end;
Nehmen wir mal diesen trivialen Fall. Alles Funktioniert. Keine Warnungen durch den Compiler. Man kann auch in der Child-Klasse die Parent-Methode mittels inherited nutzen.

Auch wenn ich nun beim Parent ein "virtual" hinzufüge, ändert sich nichts am Verhalten des Programms, außer dass es eine W1010 Warnung vom Compiler gibt.

Die Methode "Something" wird mit und ohne "virtual" verdeckt und die verdeckte Methode kann so oder so mit "inherited" verwendet werden.

Wieso ist sie angeblich in dem einen Fall "verdeckt" und im anderen Fall nicht? Was bringt die Angabe von "virtual", wenn man die Methode doch ohnehin mittels inherited weiter verwenden kann?

Delphi-Quellcode:
 TMyParent = class(TObject)
  public
    procedure Something; virtual;
  end;

 TMyChild = class(TMyParent)
  public
    procedure Something; { kein "override" nötig! }
  end;
Was ändert sich durch "virtual"?

In so vielen Büchern steht, dass eine abgeleitete Klasse eine virtuelle Prozedur überschreiben kann. Aber das geht doch auch ohne "virtual", siehe Beispiel!

Oder zurück zur Titelfrage: Was würde passieren, wenn "TObject.Destroy" nicht virtual deklariert wäre und abgeleitete Klassen einfach wie im obigen Beispiel Destroy neu implementieren und über inherited die Parent-Prozedur aufrufen würden? Wäre irgendwas anders, als es jetzt ist?

Geändert von HJay (22. Mai 2017 um 14:20 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Olli73
Olli73

Registriert seit: 25. Apr 2008
Ort: Neunkirchen
662 Beiträge
 
#12

AW: Warum virtual / override bei destructor / constructor?

  Alt 22. Mai 2017, 14:23
Delphi-Quellcode:
procedure Test;
var
  A: TMyParent;
begin
  A := TMyChild.Create;
  try
    A.Something;
  finally
    A.Free;
  end;
end;
Das führst jetzt einmal ohne und einmal mit virtual+override aus, dann siehst du im Ergebnis den Unterschied.
  Mit Zitat antworten Zitat
HJay

Registriert seit: 7. Dez 2009
172 Beiträge
 
Delphi XE7 Enterprise
 
#13

AW: Warum virtual / override bei destructor / constructor?

  Alt 22. Mai 2017, 14:31
Vielen Dank, OK, das verstehe ich jetzt! Gute Erklärung als Beispiel!

Umso mehr erscheint es doch aber dann als Fehler, dass TObject.Create eben gerade nicht virtuell deklariert ist...? Das war ja die Titelfrage dieses Threads...
  Mit Zitat antworten Zitat
Benutzerbild von Olli73
Olli73

Registriert seit: 25. Apr 2008
Ort: Neunkirchen
662 Beiträge
 
#14

AW: Warum virtual / override bei destructor / constructor?

  Alt 22. Mai 2017, 14:40
Umso mehr erscheint es doch aber dann als Fehler, dass TObject.Create eben gerade nicht virtuell deklariert ist...? Das war ja die Titelfrage dieses Threads...
Siehe Beiträge #7 und #9.
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
10.993 Beiträge
 
Delphi 12 Athens
 
#15

AW: Warum virtual / override bei destructor / constructor?

  Alt 22. Mai 2017, 15:14
Umso mehr erscheint es doch aber dann als Fehler, dass TObject.Create eben gerade nicht virtuell deklariert ist...? Das war ja die Titelfrage dieses Threads...
Gut, dann versuch doch mal in eigenen Worte zu erläutern, für welche Anwendungsfälle man einen virtuellen Konstruktor überhaupt wirklich brauchen würde (und ich meine nicht, es Anfängern leichter zu machen).
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

Registriert seit: 12. Aug 2003
Ort: Soest
4.008 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#16

AW: Warum virtual / override bei destructor / constructor?

  Alt 22. Mai 2017, 16:08
Vielen Dank, OK, das verstehe ich jetzt! Gute Erklärung als Beispiel!

Umso mehr erscheint es doch aber dann als Fehler, dass TObject.Create eben gerade nicht virtuell deklariert ist...? Das war ja die Titelfrage dieses Threads...
Weil in aller Regel der Konstruktor mit Angabe der Klasse davor erstellt wird (TChild.Create oder TParent.Create). Dafür braucht er nicht virtual zu sein. Virtual muss er nur sein, wenn du über eine Metaklasse (TClass btw class of ...) den Konstruktor aufrufst (wie zuvor schon gezeigt). Der Destruktor wird aber auf der Instanzvariable aufgerufen, also muss ein virtual dispatch ausgeführt werden, damit auch der richtige Destruktor läuft (auch schon oben erklärt).
Stefan
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.114 Beiträge
 
Delphi 12 Athens
 
#17

AW: Warum virtual / override bei destructor / constructor?

  Alt 22. Mai 2017, 17:12
Außerdem gibt es nicht nur einen Constructor und die rufen sich auch noch oft gegenseitig auf ... weißt du was das für ein Chaos wird, wenn die alle Virtuell wären, dann weiß man schnell garnicht mehr was in welcher Reihenfolge aufgerufen wird.

Der Destructor heißt in der Regel immer gleich und hat keine Parameter, also geht es da immer schön gradlinig nach oben durch.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests

Geändert von himitsu (22. Mai 2017 um 17:15 Uhr)
  Mit Zitat antworten Zitat
HJay

Registriert seit: 7. Dez 2009
172 Beiträge
 
Delphi XE7 Enterprise
 
#18

AW: Warum virtual / override bei destructor / constructor?

  Alt 22. Mai 2017, 17:57
OK, vielen Dank an alle für Eure Erklärungen!
  Mit Zitat antworten Zitat
freimatz

Registriert seit: 20. Mai 2010
1.378 Beiträge
 
Delphi 11 Alexandria
 
#19

AW: Warum virtual / override bei destructor / constructor?

  Alt 23. Mai 2017, 11:33
Bei uns in der Firma ist es Pflicht constructoren virtuell zu machen mit Verweis auf http://www.delphipraxis.net/668250-post12.html
(reintroduce ist schon noch erlaubt.)
  Mit Zitat antworten Zitat
Alallart

Registriert seit: 8. Dez 2015
153 Beiträge
 
#20

AW: Warum virtual / override bei destructor / constructor?

  Alt 14. Jan 2023, 12:51
Bin heute zufällig auf diesen Thread gestoßen und weil die Frage interessant klang, durchgelesen. Auch wenn der Thread inzwischen alt ist, glaube ich, dass die Frage noch einige interessiert. Vielleicht liest es jemand noch. Vielleicht kann ich eine einfache Antwort geben. Falls ich richtigliege, wäre es nett wenn es einer bestätigen könnte, ansonsten wiedersprechen. Ich denke Anwort #4 sagt bereits wieso es so ist, aber etwas allgemein.

Warum also sind Destruktoren virtual und Konstruktoren nicht? Die kurze Antwort: Konstruktoren müssen es nicht, Destruktoren sollten es, sonst könnte es sein, dass die nicht korrekt ausgeführt werden.

Tabelle siehe Anhang!

Um das zu verstehen habe ich eine kleine Tabelle erstellt. Sie beschreibt vier Klassen. TKlasseC ist von TKlasseB abgeleitet, TKlasseB von TKlasseA, TKlasseA von TObject. Alle Klassen haben einen Konstruktor Create und einen Destruktor Destroy.

Den Konstruktor Create ignorieren wir vorerst, und konzentrieren und auf Destroy. Wie gesagt, alle Klassen haben ein Create und ein Destroy, aber nur TObject hat Free. Man könnte auch ein eigenes Free in jede Klasse einbauen (also A, B und C), aber wer macht das schon? Es gibt ja das Free von TObject, und das reicht. Es macht ja seinen Job. Also wozu ein eigenes Free programmieren?

Und hier liegt meiner Meinung nach des Pudels Kern. Destroy ist wegen Free virtual (u.a.).

Nehmen wir mal an ich erstelle die drei Klassen und gebe sie zum Schluss mit Free frei. Jede Klasse (also A, B und C) hat in Create und Destroy einen Code der beim freigeben ausgeführt werden muss. Das Destroy von TObject ist leer, aber alle anderen haben etwas Wichtiges drinstehen. Wenn ich also KlasseC freigebe, müssten die Destroys alle vier Klassen ausgeführt werden. Weil sie voneinander abstammen. Werden sie das?

Nehmen wir mal an keiner der Destroys (also von A, B, C und TObject) ist virtual. Was wird alles ausgeführt? Meiner Meinung nach nur das Destroy von TObject. Sonst keines.

Warum? Weil in dem Fall das Destroy von TObject statisch ist, also auch eine feste Adresse auf seinen Code bekommt. Warum sollen in dem Fall die anderen Destroys ausgeführt werden? Rufe ich aus TKlasseC Free auf, ruft das das Destroy von TObject auf. Einzig und alleine. Wegen der statischen Adresse. Das Free hat TKlasseC von TObject geerbt, d. h. dieses Free greift eigentlich nur auf das Destroy von TObject zu.

Nehmen wir jetzt an das Destroy von TObject ist virtual und von den anderen override. Ruft KlasseC Free auf, ruft das, wie gehabt, das Destroy von TObject auf, weil Free eine Methode von TObject ist. Das hat aber keine statische Adresse auf seinen Code, weil virtual. Also geht es weiter zu Destroy von TKlasseA. Auch das ist nicht statisch, also geht es weiter an das Destroy von KlasseB, und zuletzt an das von KlasseC. Erst das hat eine Adresse auf seinen Code. In dem Fall den Code von Destroy von TKlasseC. Das Programm sagt "To". Nun geht es wieder abwärts, wegen inherited. Nun wird Destroy von TKlasseB aufgerufen, das Programm sagt "Tac", dann von TKlasseA ("Tic") und zuletzt von TObject.

Deses Problem hat Create nicht, weil Create direkt aus der jeweiligen Klasse aufgerufen wird. Inherited erledigt dann den Rest, d. h es werden auch die Create von den abgeleiteten Klassen ausgeführt. Destroy wird nicht direkt aufgerufen, sondern über Free. Und das ist nur in TObject vorhanden. Würde man Destroy direkt aufrufen, müsste es nicht virtual sein.

Würde man ein Free in TKlasseC programmieren, würde es direkt auf das Destroy von TKlasseC zugreifen. In dem Fall könnte man drauf verzichten Destroy virtual zu machen.

Das ist meine Erklärung dazu. Falls ich richtigliege, kann es einer bitte bestätigen? Würde mich interessieren.
Miniaturansicht angehängter Grafiken
img.png  

Geändert von Alallart (14. Jan 2023 um 12:55 Uhr)
  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 23:08 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