Delphi: Verstehen Bauer
-
28-09-2019 - |
Frage
ich suche auf verstehen
- virtuelle
- Überschreiben
- Überlastung
- reintroduce
, wenn auf Objektkonstruktoren angewendet. Jedes Mal, wenn ich zufällig Schlüsselwörter, bis der Compiler Shuts aufaddieren -. Und (nach 12 Jahren Entwicklung mit Delphi) ich würde vielmehr wissen, was ich tue, anstatt zu versuchen, die Dinge dem Zufall
eine hypothetische Menge von Objekten gegeben:
TComputer = class(TObject)
public
constructor Create(Cup: Integer); virtual;
end;
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer; Teapot: string); virtual;
end;
TiPhone = class(TCellPhone)
public
constructor Create(Cup: Integer); override;
constructor Create(Cup: Integer; Teapot: string); override;
end;
Die Art, wie ich sie zu verhalten will, ist wohl offensichtlich aus den Erklärungen, aber:
-
TComputer
hat den einfachen Konstruktor und Nachkommen kann sie außer Kraft setzen -
TCellPhone
hat einen alternativen Konstruktor und Nachkommen kann es außer Kraft setzen -
TiPhone
überschreibt die beiden Konstrukteure, die geerbte Version jedes Aufruf
Nun wird dieser Code nicht kompilieren. Ich möchte verstehen, Warum es funktioniert nicht. Ich möchte auch die richtige Art und Weise verstehen, Konstrukteuren außer Kraft zu setzen. Oder vielleicht könnten Sie nie Konstrukteurs außer Kraft setzen? Oder vielleicht ist es durchaus akzeptabel, außer Kraft zu setzen Bauer? Vielleicht sollten Sie nie mehrere Konstrukteure haben, vielleicht ist es völlig akzeptabel ist, mehrere Konstrukteure zu haben.
Ich möchte die Warum verstehen. Fixing es wäre dann klar sein.
Siehe auch
- Delphi: Wie Vorfahren Bauer verstecken
- Wiedereinführung Funktionen in Delphi
- Delphi: Wie einen anderen Konstruktor hinzuzufügen ein Nachkomme?
Edit: ich suche auch einige Überlegungen in der Größenordnung von virtual
zu bekommen, override
, overload
, reintroduce
. Denn bei dem Versuch, alle Kombinationen von Keywords, die Anzahl der Kombinationen explodiert:
- virtual; Überlastung;
- virtual; außer Kraft setzen;
- überschreiben; Überlastung;
- überschreiben; virtuell;
- virtual; außer Kraft setzen; Überlastung;
- virtual; Überlast; außer Kraft setzen;
- Überlastung; virtuelle; außer Kraft setzen;
- überschreiben; virtuelle; Überlastung;
- überschreiben; Überlast; virtuell;
- Überlastung; außer Kraft setzen; virtuell;
- etc
Edit 2: Ich denke, wir sollten beginnen mit " wird die Objekthierarchie sogar möglich gegeben ?" Wenn nicht, warum nicht? Zum Beispiel ist es grundsätzlich falsch einen Konstruktor von einem Vorfahren zu haben?
TComputer = class(TObject)
public
constructor Create(Cup: Integer); virtual;
end;
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer; Teapot: string); virtual;
end;
ich würde erwarten, dass TCellPhone
jetzt zwei Konstrukteuren hat. Aber ich kann nicht die Kombination von Schlüsselwort in Delphi finden, um es denkt, das ist eine gültige Sache zu tun. grundlegend falsch Bin ich denke ich zwei Konstrukteure hier in TCellPhone
haben kann?
Hinweis: Alles unterhalb dieser Linie ist nicht unbedingt die Antwort erforderlich Frage - aber es hilft zu erklären, mein Denken. Vielleicht können Sie sehen, basierend auf meine Denkprozesse, was fundemantale Ich vermisse, dass alles klar macht.
Nun sind diese Erklärungen nicht kompilieren:
//Method Create hides virtual method of base type TComputer:
TCellPhone = class(TComputer)
constructor Create(Cup: Integer; Teapot: string); virtual;
//Method Create hides virtual method of base type TCellPhone:
TiPhone = class(TCellPhone)
public
constructor Create(Cup: Integer); override;
constructor Create(Cup: Integer; Teapot: string); overload; <--------
end;
Also zuerst werde ich versuchen, TCellPhone
fixieren. Ich werde beginnen, indem zufällig Hinzufügen des overload
Schlüsselwort (ich weiß, ich will nicht reintroduce
weil das den anderen Konstruktor verstecken würde, was ich will nicht):
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer; Teapot: string); virtual; overload;
end;
Aber das fehlschlägt. Field definition not allowed after methods or properties
ich weiß aus Erfahrung, dass, obwohl ich nach einer Methode oder Eigenschaft nicht ein Feld habe, wenn ich die Reihenfolge der virtual
und overload
Schlüsselwort umkehren: Delphi wird zum Schweigen bringen:
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;
Aber ich immer noch den Fehler:
Die Methode ‚Create‘ Häute virtuelle Methode des Basistypen ‚TCOMPuter "
Also ich versuche, beiden Schlüsselwörter zu entfernen:
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer; Teapot: string);
end;
Aber ich immer noch den Fehler:
Die Methode 'Create' Häute virtuelle Methode des Basistypen 'TComputer'
Also ich mich damit abfinden, jetzt versucht reintroduce
:
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer; Teapot: string); reintroduce;
end;
Und jetzt TCellPhone compiliert, aber es hat alles viel schlimmer für TiPhone gemacht:
TiPhone = class(TCellPhone)
public
constructor Create(Cup: Integer); override; <-----cannot override a static method
constructor Create(Cup: Integer; Teapot: string); override; <-----cannot override a static method
end;
Beide klagen darüber, dass ich sie nicht außer Kraft setzen kann, so dass ich das override
Stichwort entfernen:
TiPhone = class(TCellPhone)
public
constructor Create(Cup: Integer);
constructor Create(Cup: Integer; Teapot: string);
end;
Aber jetzt ist die zweite schaffen, sagt es mit Überlastung markiert werden muß, was ich tue (in der Tat i sowohl als Überlast markieren würde, da ich weiß, was passieren wird, wenn ich nicht tun):
TiPhone = class(TCellPhone)
public
constructor Create(Cup: Integer); overload;
constructor Create(Cup: Integer; Teapot: string); overload;
end;
Alle alles ist gut in dem interface
Abschnitt. Leider werden meine Ausführungen nicht. Mein einziger Parameter Konstruktor TiPhone kann den geerbten Konstruktor nicht aufrufen:
constructor TiPhone.Create(Cup: Integer);
begin
inherited Create(Cup); <---- Not enough actual parameters
end;
Lösung
Ich sehe zwei Gründe, um Ihre ursprünglichen Satz von Deklarationen kompilieren sollte nicht sauber:
-
Es sollte eine Warnung sein in
TCellPhone
, dass sein Konstruktor versteckt die Methode der Basisklasse. Dies liegt daran, die Basis-Klasse Methode ist virtuelle , und die Compiler Sorgen, dass Sie ohne Überschreiben die Basisklasse Methode a neue Methode mit dem gleichen Namen sind einzuführen. Es spielt keine Rolle, dass die Unterschriften unterscheiden. Wenn Ihre Absicht ist in der Tat die Methode der Basisklasse zu verstecken, dann müssen Sie verwendenreintroduce
auf der Nachkomme Erklärung, als eine Ihrer blinden Vermutungen zeigte. Der einzige Zweck dieser Richtlinie ist es, die Warnung zu unterdrücken; es hat keinen Einfluss auf das Laufzeitverhalten.Das Ignorieren, was mit
TIPhone
passieren wird später die folgendeTCellPhone
Erklärung ist das, was man sich wünschen kann. Es verbirgt sich die Vorfahren Methode, aber Sie wollen es auch virtuell sein. Es wird nicht die virtualness der Vorfahren Methode erbt, weil sie zwei völlig getrennte Verfahren, die nur die gleichen Namen haben passieren. Daher müssen Sievirtual
auf die neue Erklärung auch nutzen.TCellPhone = class(TComputer) public constructor Create(Cup: Integer; Teapot: string); reintroduce; virtual; end;
Der Basisklassenkonstruktor,
TComputer.Create
wird auch ein Verfahren zum Verstecken seine Vorfahren,TObject.Create
, aber da das Verfahren inTObject
nicht virtuell ist, wird der Compiler nicht darüber warnen. Ausblenden von nicht-virtuellen Methoden geschieht die ganze Zeit und ist in der Regel unauffällig. -
Sie sollten einen Fehler angezeigt in
TIPhone
, weil es nicht mehr jeden einargumentigen Konstruktor außer Kraft zu setzen. Sie versteckte es inTCellPhone
. Da Sie zwei Konstrukteure haben wollen,reintroduce
klar nicht die richtige Wahl früher zu verwenden. Sie wollen nicht den Basisklassenkonstruktor zu verbergen; Sie wollen, dass es mit einem anderen Konstruktor erweitern.Da Sie beide Konstrukteure wollen den gleichen Namen haben, müssen Sie die
overload
Richtlinie verwenden. Diese Richtlinie Bedürfnisse auf verwendet werdenalle ursprünglichen Erklärungen - das erste Mal, jede einzelne Signatur eingeführt wirdnachfolgenden Erklärungen in Nachkommen. Ich dachte, es war erforderlich, auf alle Erklärungen (auch die Basisklasse), und es tut nicht weh, das zu tun, aber ich denke, es ist nicht erforderlich. Also, Ihre Erklärungen sollten wie folgt aussehen:TComputer = class(TObject) public constructor Create(Cup: Integer); overload; // Allow descendants to add more constructors named Create. virtual; // Allow descendants to re-implement this constructor. end; TCellPhone = class(TComputer) public constructor Create(Cup: Integer; Teapot: string); overload; // Add another method named Create. virtual; // Allow descendants to re-implement this constructor. end; TiPhone = class(TCellPhone) public constructor Create(Cup: Integer); override; // Re-implement the ancestor's Create(Integer). constructor Create(Cup: Integer; Teapot: string); override; // Re-implement the ancestor's Create(Integer, string). end;
Moderne Dokumentation sagt, was um alles in gehen sollte:
reintroduce ; Überlast ; Bindung ; Aufrufkonvention ; abstrakt ; Warnung
Dabei steht Bindung ist virtuelle , dynamische oder Überschreibung ; Aufrufkonvention ist registrieren , pascal , cdecl , stdcall oder safecall ; und Warnung ist Plattform , deprecated oder Bibliothek .
Das sind sechs verschiedene Kategorien, aber in meiner Erfahrung ist es selten mehr als drei auf jeder Erklärung zu haben. (Zum Beispiel Funktionen, dass Bedarf Aufrufkonventionen angegeben sind wahrscheinlich diese Methoden nicht, so dass sie nicht virtuell sein können.) Ich habe nie den Auftrag erinnern; Ich habe es nie dokumentierte bis heute zu sehen. Stattdessen denke ich, es hilfreicher ist jede Richtlinie der Zweck merken . Wenn Sie merken, welche Richtlinien Sie benötigen für verschiedene Aufgaben, werden Sie am Ende mit nur zwei oder drei, und dann einen gültigen Auftrag zu erhalten, es ist ziemlich einfach zu experimentieren. Der Compiler kann mehrere Aufträge annehmen, aber keine Sorge - um bei der Bestimmung Bedeutung ist nicht wichtig. Jede Bestellung der Compiler akzeptiert die gleiche Bedeutung hat wie jeder andere haben (mit Ausnahme der Aufrufkonventionen, wenn Sie erwähnen,mehr als einer von denen nur die letzten zählt, so tut das nicht).
Also, dann müssen Sie nur noch den Zweck der jeweiligen Richtlinie erinnern und darüber nachdenken, welche diejenigen zusammen keinen Sinn machen. Zum Beispiel können Sie nicht reintroduce
und override
zugleich verwenden, weil sie entgegengesetzte Bedeutungen haben. Und Sie können nicht virtual
und override
zusammen verwenden, da das eine das andere bedeutet.
Wenn Sie viele Richtlinien haben stapeln, können Sie immer schneiden overload
aus dem Bild heraus, während Sie den Rest der Richtlinien erarbeiten Sie benötigen. Geben Sie Ihre Methoden verschiedene Namen, herauszufinden, welche der andere Richtlinien sie selbst brauchen, und dann overload
wieder hinzufügen, während Sie geben sie alle die gleichen Namen wieder.
Andere Tipps
Beachten Sie, dass ich nicht über 5 Delphi, so dass ich meine Antworten bin stützen aus der neuesten Version, Delphi XE. Ich glaube nicht, dass wirklich keinen Unterschied hier machen, aber wenn es funktioniert, haben Sie gewarnt. :)
Dies ist vor allem auf Basis von http://docwiki.embarcadero.com/RADStudio/en/Methods , die die aktuelle Dokumentation, wie Methoden der Arbeit ist. Ihre Delphi 5-Hilfedatei hat wahrscheinlich etwas ähnliches wie dies auch.
Zunächst einmal, ein virtueller Konstruktor kann nicht viel Sinn hier machen. Es gibt ein paar Fälle, in denen Sie dies wünschen, aber das ist wahrscheinlich nicht ein. Schauen Sie sich auf http://docwiki.embarcadero.com/RADStudio/en/Class_References für eine situtation, wo Sie einen virtuellen Konstruktor brauchen - wenn Sie immer die Art Ihrer Objekte wissen bei der Codierung jedoch nicht wahr
. Das Problem, das Sie dann in dem 1-Parameter-Konstruktor erhalten ist, dass Ihre Eltern-Klasse kein 1-Parameter-Konstruktor hat sich - geerbt Konstrukteuren nicht ausgesetzt sind. Sie können nicht inherited
verwenden, um mehrere Ebene in der Hierarchie nach oben, können Sie nur Ihre unmittelbar Eltern anrufen. Sie müssen den 2-Parameter-Konstruktor mit einigem Standardwert nennen, oder einen 1-Parameter-Konstruktor TCellPhone auch hinzufügen.
In der Regel haben die vier Schlüsselwörter folgende Bedeutung:
-
virtual
- Markieren Sie diese als eine Funktion, wo Sie wollen Laufzeit Dispatching (polymorphes Verhalten erlaubt). Dies ist nur für die erste Definition, nicht, wenn sie in Unterklassen überschrieben. -
override
-. Eine neue Implementierung für eine virtuelle Methode Geben Sie -
overload
-. Markieren Sie eine Funktion mit dem gleichen Namen wie eine andere Funktion, sondern eine andere Parameterliste -
reintroduce
-. Sagen Sie der Compiler Sie eigentlich bestimmt eine virtuelle Methode zu verstecken, anstatt nur zu Versorgungsoverride
vergessen
Die Bestellung erforderlich ist, in der Dokumentation beschrieben:
können Methodendeklarationen enthalten spezielle Richtlinien, die nicht verwendet werden mit anderen Funktionen oder Prozeduren. Richtlinien sollten in der Klasse erscheinen nur Erklärung, nicht in der Definition Erklärung und sollte immer sein, aufgeführt in der folgenden Reihenfolge:
reintroduce; Überlast; Bindung; Aufrufkonvention; abstrakt; Warnung
, wo die Bindung ist virtuell, dynamisch oder außer Kraft setzen; Aufrufkonvention ist Register, pascal, cdecl, stdcall oder safecall; und Warnung Plattform, Veraltete oder Bibliothek.
Dies ist eine funktionierende Implementierung der Definitionen gesucht:
program OnConstructors;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TComputer = class(TObject)
public
constructor Create(Cup: Integer); virtual;
end;
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer; Teapot: string); reintroduce; overload; virtual;
end;
TiPhone = class(TCellPhone)
public
constructor Create(Cup: Integer); overload; override;
constructor Create(Cup: Integer; Teapot: string); override;
end;
{ TComputer }
constructor TComputer.Create(Cup: Integer);
begin
Writeln('Computer: cup = ', Cup);
end;
{ TCellPhone }
constructor TCellPhone.Create(Cup: Integer; Teapot: string);
begin
inherited Create(Cup);
Writeln('Cellphone: teapot = ', Teapot);
end;
{ TiPhone }
constructor TiPhone.Create(Cup: Integer);
begin
inherited Create(Cup);
Writeln('iPhone: cup = ', Cup);
end;
constructor TiPhone.Create(Cup: Integer; Teapot: string);
begin
inherited;
Writeln('iPhone: teapot = ', Teapot);
end;
var
C: TComputer;
begin
C := TComputer.Create(1);
Writeln; FreeAndNil(C);
C := TCellPhone.Create(2);
Writeln; FreeAndNil(C);
C := TCellPhone.Create(3, 'kettle');
Writeln; FreeAndNil(C);
C := TiPhone.Create(4);
Writeln; FreeAndNil(C);
C := TiPhone.Create(5, 'iPot');
Readln; FreeAndNil(C);
end.
mit den Ergebnissen:
Computer: cup = 1
Computer: cup = 2
Computer: cup = 3
Cellphone: teapot = kettle
Computer: cup = 4
iPhone: cup = 4
Computer: cup = 5
Cellphone: teapot = iPot
iPhone: teapot = iPot
Der erste Teil ist in Übereinstimmung mit
Verwendung Überlastung beide, es ist die Art, wie ich es tun, und es funktioniert. erinnere mich nicht den gleichen Namen für zwei unterschiedlichen Herstellern zu verwenden, constructor Create; Overload
; <- Nutzung überlastete hier constructor Values; Overload;
<- und hier