Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Einsatzgebiete von statischen und virtuellen Methoden ???
delphisual - Do 05.02.04 19:52
Titel: Einsatzgebiete von statischen und virtuellen Methoden ???
Hallo zusammen,
ich habe ein Paar Fragen bezüglich OOP um genauer zu sein beziehen sich meine Fragen eher auf die Methoden in OOP.
Wo liegt der unterschied zwischen virtuellen und statischen Methoden?
Ich weis dass auf virtuellen Methoden über eine VMT zugegriffen wird und die Statischen Methoden direkt angesprochen werden.
Ich bin bis jetzt immer davon ausgegangen das die Virtuellen Methoden extra für die überschreibung, von Methoden des Vorfahren durch die Gleichnamige Methoden des Nachfahren, bestimmt sind.
Es scheint aber genau andersrum zu sein!?
Beispiel1:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99:
| unit uMutter;
interface
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
type TMutter = class(TObject) private public procedure Alter;
end;
implementation
procedure TMutter.Alter; begin ShowMessage('Alter ist 45'); end; end.
unit uKind;
interface
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, uMutter;
type TKind = class(TMutter) private public procedure Alter;
end;
implementation
procedure TKind.Alter; begin ShowMessage('Alter ist 15'); end;
end.
unit Unit1;
interface
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private public end;
var Form1: TForm1;
implementation
{$R *.DFM}
uses uMutter,uKind;
procedure TForm1.Button1Click(Sender: TObject); var Mutter: TMutter; Kind: TKind; begin Mutter:=TMutter.Create; Mutter.Alter; Kind:=TKind.Create; Kind.Alter; end;
end. |
Nach dem Compilieren habe ich festgestellt das die Methode TMutter.Alter von TKind.Alter überschrieben wurde, ob woll ich die Methode nicht als virtual deklariert habe!
Beispiel2:
Ich habe die Methode TMutter.Alter an TKind vererbt und die erweitert:
Delphi-Quelltext
1: 2: 3: 4: 5:
| procedure TKind.Alter; begin inherited; ShowMessage('Alter ist 15'); end; |
Funktioniert auch ohne direktive virtual!
Beispiel3:
Ich erzeuge das Objekt Mutter mit dem Zeiger auf TKind Klasse.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| procedure TForm1.Button1Click(Sender: TObject); var Mutter: TMutter; Kind: TKind; begin Mutter:=TKind.Create; Mutter.Alter; end; |
das Funktioniert doch ich habe trotzdem die Methode von TMutter.Alter ausgelesen!
Kenzeichne ich die Methode TMutter.Alter mit virtual, und die Methode TKind.Alter mit override, lese ich (mit dem gleichen Code) dann die Methode des TKind.Alter aus.
?: Ich habe bis jetzt immer gedacht dass, wenn ich die Methode des Vorfahren überschreiben will, ich muss die deklaration mit virtual und override machen .
1. Wie ich aber festgestellt habe hat die deklaration nur dan die wirkung wenn ich die die Methoden des Nachfahrens auslese!???
2. Wie es aussieht kann ich nicht mit statischen Methoden auf die Methoden des Nachfahrens zugreifen aber warum soll ich das wollen?
3. Bitte schreibt mir die Einatzgebiete der Statischen und virtuellen Methoden, ich weis nicht mehr weiter.
MfG
delphisual
Moderiert von
Peter Lustig: Topic verschoben
ChrHohbein - Do 05.02.04 20:04
Zu Beispiel 3.
Du hast Das objekt mutter als Objekt TMutter deklariert.
Du kannst für die die Zuweisung natürlich ein objekt nehmen was von diesesm Abgeleitet ist. er wirt für die Verwendung aber lediglich die Klasse nehmen die du ihm vorher zugewiesen hast nämlich TMutter.
MSCH - Do 05.02.04 21:57
Zitate:
| Zitat: |
Mit Hilfe der Direktiven virtual und dynamic können Methoden als virtuell oder dynamisch deklariert werden. Virtuelle und dynamische Methoden können im Gegensatz zu statischen Methoden in abgeleiteten Klassen überschrieben werden. Beim Aufrufen einer überschriebenen Methode bestimmt nicht der deklarierte, sondern der aktuelle Typ (also der Typ zur Laufzeit) der im Aufruf verwendeten Klassen- bzw. Objektvariable, welche Implementierung aktiviert wird.
Um eine Methode zu überschreiben, braucht sie nur mit der Direktiven override erneut deklariert zu werden. Dabei müssen Reihenfolge und Typ der Parameter sowie der Typ des Rückgabewertes (falls vorhanden) mit der Deklaration in der Vorfahrklasse übereinstimmen. |
| Zitat: |
Unterschiede zwischen virtuellen und dynamischen Methoden
Virtuelle und dynamische Methoden sind von der Semantik her identisch. Sie unterscheiden sich nur bei der Implementierung der Aufrufverteilung zur Laufzeit. Virtuelle Methoden werden auf Geschwindigkeit, dynamische Methoden auf Code-Größe optimiert.
Im allgemeinen kann mit virtuellen Methoden polymorphes Verhalten am effizientesten implementiert werden. Dynamische Methoden sind hilfreich, wenn in einer Basisklasse eine große Anzahl überschreibbarer Methoden deklariert ist, die von vielen abgeleiteten Klassen geerbt, aber nur selten überschrieben werden. |
näheres in der OH.
grez
msch
delphisual - Do 05.02.04 22:26
Danke erstmal für Eure Antworten,
ich habe die Delphi Hilfe selber schon paar mal durchgelesen. Es ist mir auch bekannt das die dynamische Methoden bei ca. 100 Methoden 40 kb einsparen, doch es geht mir nicht darum! Mich interessiert einfach wofür ich genau die Fähigkeit brauche aus dem Mutter Objekts-Methode auf die Methode des Kinds-Objekt zuzugreifen. Den wenn ich einfach nur die Methode des Vorfahren im Nachfahr-Objekt nutzen will brauche ich keine virtuelle Deklaration, möchte ich aber auf die Methode des Nachkommen aus dem Objekt des Vorfahren zugreifen so ist die virtual Deklaration der Methode ein Mus!
Normale weise ist das doch so, dass man mit Nachfahr-Objecten arbeitet die von Vorfahren stammen und nicht andersrum! Es ist auch öfters anzusehen das die Methoden als abstract deklariert werden, da ist aber der Sinn ganz klar Kompatibilität von verschiedenen Nachverfahren zueinander. Werden vielleicht aus dem Gleichen Grund auch die virtuellen Methoden verwendet?
Wozu brauchen im Hierarchie der VCL höher angesiedelte Objekte die Zugriffmöglichkeit auf die weiter unten angesiedelte Objekte, den wie ich es verstehe genau das mache ich mit Deklaration von virtual Methoden?
Mit freundlichen Grüßen
delphisual
MSCH - Do 05.02.04 22:39
hi, wenn ich's richtig lese, von dem Parent-Object auf Child-Object-Functionen zugreifen? Das geht nicht. Nur umgekehrt.
Eine virtuelle Methode braucht man, wenn man in der abgeleiteten Child-Methode die gleichnamige des Parents aufrufen möchte.
(inherited)
beispiel:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
| Type TmyParent= Class Titel: String; procedure Paint; virtual; end; TmyChild = Class(TmyParent) procedure Paint;virtual; end;
procedure TmyParent.Paint; begin end;
procedure TmyChild.Paint; begin inherited; end; |
Simpel aus gedrückt, Basisklassen implementieren nur rudimentär die Funktion; in abgeleiteten Klassen wird diese dann erweitert. Schau dir einfach mal die Quelltexte der VCL an.
grez
msch
AndyB - Fr 06.02.04 00:23
| MSCH hat folgendes geschrieben: |
Eine virtuelle Methode braucht man, wenn man in der abgeleiteten Child-Methode die gleichnamige des Parents aufrufen möchte.
(inherited) |
Dazu brauch ich keine virtuelle Methode.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| type TBase = class(TObject) procedure TuWas; end;
TDerived = class(TBase) procedure TuWas; end;
procedure TBase.TuWas; begin ShowMessage('Ich mach was.'); end;
procedure TDerived.TuWas; begin inherited TuWas; end; |
Virtuelle Methoden ermöglichen es eine Basisklasse zu schreiben, die es den abgeleiteten Klassen ermöglicht eine bestehende Funktionalität zu erweitern. So weiß z.B. eine Basisklasse TFileStorage wie sie eine einen Stream in eine Datei schreibt. Wie die Daten in den Stream kommen überlässt sie den Unterklassen.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:
| type TFileStorage = class(TObject) procedure SaveToFile(const FileName: string); procedure SaveToStream(Stream: TStream); virtual; end;
procedure TFileStorage.SaveToFile(const FileName: string); var Stream: TStream; begin Stream := TFileStream.Create(FileName, fmCreate); try SaveToStream(Stream); finally Stream.Free; end; end;
procedure TFileStorage.SaveToStream(Stream: TStream); begin end; |
Wenn man nun eine Klasse TPerson von TFileStorage ableitet, und die Methode SaveToStream überschreibt, wird automatisch die TPerson.SaveToStream Methode von TFileStorage.SaveToFile aufgerufen.
Wäre SaveToStream statisch gewesen würde egal wie oft man SaveToStream in einer Unterklasse zu überschreiben versucht immer TFileStorage.SaveToStream aufgerufen, die ja nichts macht.
Anderes Beispiel:
Schleifen sind was feines. Also möchte man seine Daten bzw. Objekte immer gerne in Listen oder Arrays halten um sie mit Schleifen zu durchlaufen. Jetzt wäre es ziemlich umständlich, wenn man ständig mit
is Prüfen müsste, dass ein Objekt von Typ TPunkt, TLinie, TRechteck, TKreis ist und dann entsprechend Typecasten. Also führt man alle Gemeinsamkeiten in einer Basisklasse zusammen, die da z.B. wären:
Delphi-Quelltext
1: 2: 3: 4:
| type TDrawable = class(TObject) procedure DrawTo(Canvas: TCanvas); virtual; end; |
Nun kann man seine Unterklassen davon ableiten und DrawTo mit den entsprechenden Zeichenbefehlen überschreiben. Hiermit erlaubt der Compiler uns folgendes zu machen:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| var a: array[0..3] of TDrawable; begin a[0] := TKreis.Create(10, 10, 5, clRed); a[1] := TRechteck.Create(5, 5, 15, 15, clBlack); a[2] := TLinie.Create(5, 5, 15, 15, clYellow); a[3] := TPunkt.Create(10, 10, clBlue);
for i := 0 to High(a) do a[i].DrawTo(Canvas); end; |
Ohne virtuelle Methoden würde das ungefähr so aussehen:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| var a: array[0..3] of TDrawable; begin a[0] := TKreis.Create(10, 10, 5, clRed); a[1] := TRechteck.Create(5, 5, 15, 15, clBlack); a[2] := TLinie.Create(5, 5, 15, 15, clYellow); a[3] := TPunkt.Create(10, 10, clBlue);
for i := 0 to High(a) do begin if a[i] is TKreis then TKreis(a[i]).DrawTo(Canvas) else if a[i] is TRechteck then TRechteck(a[i]).DrawTo(Canvas) ... end; |
wobei auf noch auf die richtige Reihenfolge geachtet werden muss, da ein TRechteck z.B. von eine TLinie abgeleitet sein kann, ...
Und was passiert, wenn man nachdem das Programm fertig ist, jetzt auf die Idee kommt, ein weiteres zeichenbares Objekt TDreieck zu erfinden. Man müsste alle Stellen im Programm durchforsten, anpassen und hoffen, dass nicht noch einer einen Einfall für ein zeichenbares Objekt hat.
delphisual - Fr 06.02.04 15:36
Hallo zusammen und danke für Eure Beteilung an der Diskussion!
Ich habe mir heute den ganzen Tag Gedanken drüber gemacht was ich mit dieser Fähigkeit anfangen will, und wenn ich das richtig verstanden habe bin ich in der Lage mir meine eigene Hierarchie so aufzubauen, dass ich von Vorfahr-Objekt auf alle Nachfahr-Objekte ohne Einschränkung zugreifen kann.
Wusste ich gar nicht das die Polymorphie so ein mächtiges Werkzeug ist, ich werde auf jeden Fall mich noch etwas damit beschäftigen.
Vielen, Vielen Dank für Eure Zeit!
super Forum hier.
Mit freundlichen Grüßen
delphisual
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!