Autor Beitrag
ub60
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 764
Erhaltene Danke: 127



BeitragVerfasst: 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):

ausblenden 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); reintroducevirtual;
  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
ausblenden Delphi-Quelltext
1:
Quadrat:=TQuadrat.Create(50,20,100);					

funktioniert,
ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 4006
Erhaltene Danke: 7

Windows 10 64 bit
C# (Visual Studio 2019 Express)
BeitragVerfasst: Sa 01.04.06 00:09 
user profile iconub60 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 102

Win 2K, Win XP
Delphi 2006 Arch., Delphi 6 Ent., MS-SQL 2005 & 2000
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 764
Erhaltene Danke: 127



BeitragVerfasst: 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.
ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 102

Win 2K, Win XP
Delphi 2006 Arch., Delphi 6 Ent., MS-SQL 2005 & 2000
BeitragVerfasst: Sa 01.04.06 15:29 
user profile iconub60 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.

user profile iconub60 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.

user profile iconub60 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.

user profile iconub60 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.
ausblenden 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:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
var
  Quadrat: TQuadrat;
  Hoehe: Integer;
begin
  Quadrat:=TQuadrat.Create(5020100);    //TQuadrat hat nur die Eigenschaft Breite
  Hoehe:=TRechteck(Quadrat).Hoehe;          //TRechteck kennt hingegen auch die Eigenschaft 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.

user profile iconub60 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 764
Erhaltene Danke: 127



BeitragVerfasst: Sa 01.04.06 18:11 
Also Axel, Danke erst mal für die lange Antwort.

user profile iconAxel 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.
user profile iconAxel 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.

user profile iconAxel 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 102

Win 2K, Win XP
Delphi 2006 Arch., Delphi 6 Ent., MS-SQL 2005 & 2000
BeitragVerfasst: So 02.04.06 11:47 
user profile iconub60 hat folgendes geschrieben:

Zitat:
Ist mir klar, aber override geht doch nur bei gleicher Parameteranzahl und gleichem Typ.
user profile iconAxel 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.

user profile iconAxel 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. :oops:

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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 764
Erhaltene Danke: 127



BeitragVerfasst: 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:
ausblenden 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); reintroducevirtual;
  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:
ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 102

Win 2K, Win XP
Delphi 2006 Arch., Delphi 6 Ent., MS-SQL 2005 & 2000
BeitragVerfasst: So 02.04.06 19:19 
user profile iconub60 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 :wink:) 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 !