Autor |
Beitrag |
ub60
      
Beiträge: 764
Erhaltene Danke: 127
|
Verfasst: Fr 31.03.06 23:35
Hallo Leute,
ich habe ein Problem zu Begriffen in der OOP.
Ich habe eine Klasse TRechteck und eine davon abgeleitete Klasse TQuadrat mit folgender Typdeklaration (Ausschnitt):
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| type TRechteck = class public constructor Create (x, y, Breite, Hoehe : Integer); virtual; end;
type TQuadrat = class(TRechteck) public constructor Create(x, y, Breite: Integer); reintroduce; virtual; end; |
Wenn ich in TQuadrat bei Create statt virtual overload schreibe, wird die Methode überladen und ich kann mit einem Objekt vom Typ TQuadrat auf beide Methoden zugreifen.
Mit dem dargestellten Quelltext kann (und will) ich das aber nicht.
Im Delphi-Handbuch steht aber:
Zitat: | Wenn in einer Methodendeklaration dieselben Bezeichner- und Parameterangaben
wie bei einer geerbten Methode ohne die Anweisung override angegeben werden,
wird die geerbte Methode durch die neue Deklaration verdeckt. Beide Methoden sind
jedoch in der abgeleiteten Klasse vorhanden … |
Dies stimmt jedoch nicht mit meinem Quelltext überein, denn
Delphi-Quelltext 1:
| Quadrat:=TQuadrat.Create(50,20,100); |
funktioniert,
Delphi-Quelltext 1:
| Quadrat:=TQuadrat.Create(50,20,100,200); |
funktioniert nicht.
Nun meine zwei Fragen:
- Nennt sich das, was ich hier gemacht habe, verdecken?
- Wenn ja, wie kann man dann die 4-Parameter-Methode bei einem Objekt der Klasse TQuadrat aufrufen?
ub60
|
|
AXMD
      
Beiträge: 4006
Erhaltene Danke: 7
Windows 10 64 bit
C# (Visual Studio 2019 Express)
|
Verfasst: Sa 01.04.06 00:09
ub60 hat folgendes geschrieben: | Zitat: | Wenn in einer Methodendeklaration dieselben Bezeichner- und Parameterangaben
wie bei einer geerbten Methode ohne die Anweisung override angegeben werden,
wird die geerbte Methode durch die neue Deklaration verdeckt. Beide Methoden sind
jedoch in der abgeleiteten Klasse vorhanden … |
|
Du hast verschiedene Parameter, also wird auch nichts verdeckt - auch wenn die Methoden gleich heißen (siehe hervorgehobener Text).
AXMD
|
|
afk
      
Beiträge: 102
Win 2K, Win XP
Delphi 2006 Arch., Delphi 6 Ent., MS-SQL 2005 & 2000
|
Verfasst: Sa 01.04.06 00:42
Du schmeißt ein paar Begriffe durcheinander:
"overload" wird zum Überladen von Methoden verwendet, also in einer Klasse zwei Methoden mit dem gleichen Namen, aber unterschiedlichen Parametern. Der Compiler entscheidet dann auf Basis der Parameter im Aufruf, welche Methode verwendet werden muß.
"override" wird zum Überschreiben von virtuellen und dynamischen Methoden verwendet, wie in Deinem Fall für Create (das "reintroduce" in Deinem Beispiel ist eigentlich falsch). Das Rechteck braucht Breite und Höhe im Konstruktor, das Quadrat nicht, daher wird der Konstruktor in der Klasse TQuadrat überschrieben.
Wenn Du eine Methode von TRechteck in einer Klasse TQuadrat aus einer Methode von TQuadrat aus aufrufen möchtest, dann geht das mit "inherited ...", wenn Du eine verdeckte Methode von Außerhalb erreichen willst, dann geht das per Typecast.
Gruß Axel
_________________ Man muß sparn wo mn knn !
|
|
ub60 
      
Beiträge: 764
Erhaltene Danke: 127
|
Verfasst: Sa 01.04.06 14:36
Erstmal vielen Dank an die zwei "Antwortgeber".
Zitat: | Du schmeißt ein paar Begriffe durcheinander:
"overload" wird zum Überladen von Methoden verwendet, also in einer Klasse zwei Methoden mit dem gleichen Namen, aber unterschiedlichen Parametern. Der Compiler entscheidet dann auf Basis der Parameter im Aufruf, welche Methode verwendet werden muß.
|
Glaube ich nicht. Wenn (wie ich geschrieben habe), statt dem virtual in der zweiten Methode overload steht, sind die Methodennamen gleich, aber in verschiedenen Klassen. Dann kann ich auch in TQuadrat die Methode Create mit 4 Parametern aufrufen (was ich vermeiden will).
Zitat: | "override" wird zum Überschreiben von virtuellen und dynamischen Methoden verwendet |
Ist mir klar, aber override geht doch nur bei gleicher Parameteranzahl und gleichem Typ.
Zitat: | (das "reintroduce" in Deinem Beispiel ist eigentlich falsch). |
Wenn ich das Ganze ohne reintroduce mache, erhalte ich einen Compilerhinweis, dass eine Methode verborgen wird.
(Methode Create verbirgt eine virtuelle Methode ...)
Zitat: | Wenn Du eine Methode von TRechteck in einer Klasse TQuadrat aus einer Methode von TQuadrat aus aufrufen möchtest, dann geht das mit "inherited ...", |
Das ist klar, das mache ich im Create von TQuadrat.
Zitat: | wenn Du eine verdeckte Methode von Außerhalb erreichen willst, dann geht das per Typecast. |
Hättest Du da ein Beispiel?
Mir fällt bei Typecast nur Folgendes ein, und das geht nicht.
Delphi-Quelltext 1: 2: 3:
| var Quadrat : TQuadrat; begin Quadrat:=(TQuadrat as TRechteck).Create(50,20,100,33); |
Nochmal meine Frage:
Was ich da programmiert habe, funktioniert und erfüllt meine Erwartungen, aber wie nennt man das?
ub60
|
|
afk
      
Beiträge: 102
Win 2K, Win XP
Delphi 2006 Arch., Delphi 6 Ent., MS-SQL 2005 & 2000
|
Verfasst: Sa 01.04.06 15:29
ub60 hat folgendes geschrieben: | Zitat: | Du schmeißt ein paar Begriffe durcheinander:
"overload" wird zum Überladen von Methoden verwendet, also in einer Klasse zwei Methoden mit dem gleichen Namen, aber unterschiedlichen Parametern. Der Compiler entscheidet dann auf Basis der Parameter im Aufruf, welche Methode verwendet werden muß. |
Glaube ich nicht. Wenn (wie ich geschrieben habe), statt dem virtual in der zweiten Methode overload steht, sind die Methodennamen gleich, aber in verschiedenen Klassen. Dann kann ich auch in TQuadrat die Methode Create mit 4 Parametern aufrufen (was ich vermeiden will). |
Das funktioniert so zwar, overload hat aber eine ganz andere Funktion und ist nicht dazu gedacht, zum Überschreiben von virtuellen Methoden verwendet zu werden. Auch wenn es vordergründig funktioniert, es ist trotzdem falsch.
ub60 hat folgendes geschrieben: | Zitat: | "override" wird zum Überschreiben von virtuellen und dynamischen Methoden verwendet |
Ist mir klar, aber override geht doch nur bei gleicher Parameteranzahl und gleichem Typ. |
Das stimmt nicht, mit override werden virtuelle und dynamische Methoden überschrieben, dabei darf sich die Anzahl und der Typ der Parameter ändern. Nur wenn override fehlt, dann müssen die Parameter identisch sein.
ub60 hat folgendes geschrieben: | Zitat: | (das "reintroduce" in Deinem Beispiel ist eigentlich falsch). |
Wenn ich das Ganze ohne reintroduce mache, erhalte ich einen Compilerhinweis, dass eine Methode verborgen wird.
(Methode Create verbirgt eine virtuelle Methode ...) |
Das ist logisch, weil Du kein override verwendet hast. Nimm "override;" statt "reintroduce; virtual;", dann kommt die Meldung nicht mehr, und das Ganze ist dann außerdem auch noch korrekt.
ub60 hat folgendes geschrieben: | Zitat: | wenn Du eine verdeckte Methode von Außerhalb erreichen willst, dann geht das per Typecast. |
Hättest Du da ein Beispiel?
Mir fällt bei Typecast nur Folgendes ein, und das geht nicht.
Delphi-Quelltext 1: 2: 3:
| var Quadrat : TQuadrat; begin Quadrat:=(TQuadrat as TRechteck).Create(50,20,100,33); | |
Damit forderst Du ein Interface vom Typ TRechteck an, was nicht funktioniert, da TRechteck eine Klasse und kein Interface ist.
Ein Typecast sieht so aus:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7:
| var Quadrat: TQuadrat; Hoehe: Integer; begin Quadrat:=TQuadrat.Create(50, 20, 100); Hoehe:=TRechteck(Quadrat).Hoehe; end; |
Wenn Du allerdings ein TQuadrat.Create(...) haben willst, das ein TRechteck erzeugt, das würde dem objektorientierten Ansatz der Programmiersprache widersprechen und geht daher nicht. Dafür mußt Du dann eben ganz einfach TRechteck.Create(...) aufrufen.
ub60 hat folgendes geschrieben: | Nochmal meine Frage:
Was ich da programmiert habe, funktioniert und erfüllt meine Erwartungen, aber wie nennt man das? |
Nimm es mir nicht übel, aber das nennt man ganz einfach "fehlerhaft".
Gruß Axel
_________________ Man muß sparn wo mn knn !
|
|
ub60 
      
Beiträge: 764
Erhaltene Danke: 127
|
Verfasst: Sa 01.04.06 18:11
Also Axel, Danke erst mal für die lange Antwort.
Axel schrieb:
Zitat: | Nimm es mir nicht übel, aber das nennt man ganz einfach "fehlerhaft". |
Das hat mich eigentlich am meisten aufgemuntert
Zitat: | Ist mir klar, aber override geht doch nur bei gleicher Parameteranzahl und gleichem Typ.
Axel schrieb:
Das stimmt nicht, mit override werden virtuelle und dynamische Methoden überschrieben, dabei darf sich die Anzahl und der Typ der Parameter ändern. Nur wenn override fehlt, dann müssen die Parameter identisch sein. |
Komisch, die Delphi-Hilfe behauptet zumindest genau das von mir gesagte:
Zitat: | Aus der Delphi-Hilfe:
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. |
Axel schrieb:
Zitat: | Das ist logisch, weil Du kein override verwendet hast. Nimm "override;" statt "reintroduce; virtual;", dann kommt die Meldung nicht mehr, und das Ganze ist dann außerdem auch noch korrekt. |
Wenn ich das mache, kommt die Meldung: "Deklaration von 'Create' unterscheidet sich von vorheriger Deklaration."
Schade, irgendwie komme ich hier nicht weiter...
Was ist denn genau fehlerhaft?
Die von Dir genannte Variante mit override funzt bei mir auf alle Fälle nicht.
ub60
|
|
afk
      
Beiträge: 102
Win 2K, Win XP
Delphi 2006 Arch., Delphi 6 Ent., MS-SQL 2005 & 2000
|
Verfasst: So 02.04.06 11:47
ub60 hat folgendes geschrieben: |
Zitat: | Ist mir klar, aber override geht doch nur bei gleicher Parameteranzahl und gleichem Typ.
Axel schrieb:
Das stimmt nicht, mit override werden virtuelle und dynamische Methoden überschrieben, dabei darf sich die Anzahl und der Typ der Parameter ändern. Nur wenn override fehlt, dann müssen die Parameter identisch sein. |
Komisch, die Delphi-Hilfe behauptet zumindest genau das von mir gesagte:
Zitat: | Aus der Delphi-Hilfe:
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. |
Axel schrieb:
Zitat: | Das ist logisch, weil Du kein override verwendet hast. Nimm "override;" statt "reintroduce; virtual;", dann kommt die Meldung nicht mehr, und das Ganze ist dann außerdem auch noch korrekt. |
Wenn ich das mache, kommt die Meldung: "Deklaration von 'Create' unterscheidet sich von vorheriger Deklaration."
Schade, irgendwie komme ich hier nicht weiter...
Was ist denn genau fehlerhaft?
Die von Dir genannte Variante mit override funzt bei mir auf alle Fälle nicht. |
Stimmt, da hatte ich mich geirrt, die Delphi-Hilfe hat natürlich Recht.
Trotzdem wird override üblicherweise bei virtuellen und dynamischen Methoden verwendet, da diese Methoden so vom Compiler verarbeitet werde, daß immer die zum Objekt gehörende Methode aufgerufen wird, unabhängig davon, ob man an der entsprechenden Codezeile des Aufrufs weiß, welches Objekt man da tatsächlich hat. Beispiel: Du kannst einer Variablen AnyObject vom Typ TObject jeden beliebigen Nachfahren, also z.B. ein TMyObject zuweisen. Wenn Du dann an irgendeiner Stelle in Deinem Programm die Methode AnyObject.Free aufrufst, dann wird tatsächlich TMyObject.Free aufgerufen, obwohl Dein Programm an der Stelle "denkt", das ist ein Objekt vom Typ TObject. Das funktioniert so, weil Free als virtual deklariert ist.
Bei Create hingegen muß Dein Programm immer wissen, welchen Typ das zu erzeugende Objekt haben soll, daher macht es eigentlich keinen Sinn, Create als virtuelle Methode zu deklarieren, und da liegt der eigentliche Fehler in Deinem Code, das hatte ich bisher übersehen, sorry.
In einigen VCL-Klassen ist Create allerdings auch als virtual deklariert. Wofür das gut ist, das entzieht sich meiner Kenntnis.
Gruß Axel
_________________ Man muß sparn wo mn knn !
|
|
ub60 
      
Beiträge: 764
Erhaltene Danke: 127
|
Verfasst: So 02.04.06 18:22
So, da lag ich doch anfangs gar nicht so falsch. Ich will (ich hoffe, das interessiert noch jemanden) das Ganze noch mal zusammenfassen.
Einige Stunden Bücherstudium haben mich zu folgenden Erkenntnissen gebracht ( Bitte schreibt mir, wenn ich irre).
Also, ich wollte Folgendes:
- Eine Klasse TRechteck sollte eine Basisklasse sein, in der man mit 4 Parametern Rechtecke erzeugt.
- Eine Klasse TQuadrat sollte von TRechteck abgeleitet werden.
- Die geerbte Methode Create der Klasse TRechteck mit 4 Parametern sollte innerhalb von TQuadrat nicht mehr aufgerufen werden können.
- Dafür sollte es eine neue Methode mit drei Parametern geben, mit denen ich ein Quadrat erstellen kann.
Das hatte ich so realisiert:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| type TRechteck = class public constructor Create (x, y, Breite, Hoehe : Integer); virtual; end;
type TQuadrat = class(TRechteck) public constructor Create(x, y, Breite: Integer); reintroduce; virtual; end; |
Obwohl mir Axel "vorgeworfen" hat, dass das fehlerhaft sei, glaube ich, folgende Erkenntnisse gesammelt zu haben:
- Die Verwendung von virtual beim Constructor ist nicht besonders sinnvoll (wie Axel ja sagte), aber auch nicht falsch.
- Im dargestellten Beispiel (mit zwei mal virtual) erzeugt Delphi nur eben zwei Einträge in der VMT (virtuellen Methodentabelle) der Klasse TQuadrat, wobei die ererbte Methode (die mit den 4 Parametern) von der neu deklarierten Methode verborgen wird.
- Das zumindest sagt auch der Compiler, deshalb das reintroduce zum Unterbinden der Compilermeldung.
Nach der Diskussion würde ich den Quelltext wie folgt schreiben:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| type TRechteck = class public constructor Create (x, y, Breite, Hoehe : Integer); end;
type TQuadrat = class(TRechteck) public constructor Create(x, y, Breite: Integer); end; |
- Damit ist der Constructor in der ersten Klasse statisch.
- In der abgeleiteten Klasse ist er wieder statisch und überschreibt damit den Constructor der ersten Klasse.
- Nach außen hin funktionieren beide Varianten gleichwertig.
- Intern ist die zweite Variante auf Grund der statischen Bindung schneller und benötigt weniger Speicher.
- In der ersten Variante wurden die Konstruktoren virtuell deklariert, was hier eigentlich nicht benötigt wird, aber auch nicht schadet.
So, und wenn Ihr jetzt Fehler in meinen Ausführungen findet, wäre es nett mir das zu sagen.
ub60
|
|
afk
      
Beiträge: 102
Win 2K, Win XP
Delphi 2006 Arch., Delphi 6 Ent., MS-SQL 2005 & 2000
|
Verfasst: So 02.04.06 19:19
ub60 hat folgendes geschrieben: | In der ersten Variante wurden die Konstruktoren virtuell deklariert, was hier eigentlich nicht benötigt wird, aber auch nicht schadet. |
Schadet hinsichtlich Geschwindigkeit schon ein wenig, hast Du aber bereits erwähnt. Außerdem ist es eben einfach nur überflüssig, was (wie bei mir geschehen  ) zu Verwirrung führen kann. Ansonsten ist das eine lehrbuchmäßige Beschreibung des Sachverhalts, meinen Glückwunsch.
Gruß Axel
PS: Ich wollte Dir nichts "vorwerfen", war vieleicht ein wenig blöd von mir formuliert.
_________________ Man muß sparn wo mn knn !
|
|
|