2014-05-13 5 views
5

Der Ausgang der Konsolenanwendung istPolymorphismus und Vererbung mit Klassenreferenz? unter

Parent 
Parent 
Parent 

statt

Parent 
Child1 
Child2 

Warum sollte dies geschehen? Außerdem, wie man die beabsichtigte Ausgabe erhält? Danke vielmals !

PS: Noch immer keine Ahnung, nach dem Lesen dieses related SO post ...

program Project1; 

{$APPTYPE CONSOLE} 

type 
    TParent = class; 
    TParentClass = class of TParent; 

    TParent = class 
    public 
    ID: string; 
    constructor Create; 
    end; 

    TChild1 = class(TParent) 
    public 
    constructor Create; 
    end; 

    TChild2 = class(TParent) 
    public 
    constructor Create; 
    end; 

constructor TParent.Create; 
begin 
    ID := 'Parent'; 
end; 

constructor TChild1.Create; 
begin 
    ID := 'Child1'; 
end;  

constructor TChild2.Create; 
begin 
    ID := 'Child2'; 
end; 

procedure Test(ImplClass: TParentClass); 
var 
    ImplInstance: TParent; 
begin 
    ImplInstance := ImplClass.Create; 
    WriteLn(ImplInstance.ID); 
    ImplInstance.Free; 
end; 

begin 
    Test(TParent); 
    Test(TChild1); 
    Test(TChild2); 
    Readln; 
end. 
+1

Hinweis Ich habe den Code bearbeitet. Sie wollten nicht in jeder abgeleiteten Klasse neue 'ID'-Felder hinzufügen, die das in' TParent' deklarierte verbergen. –

Antwort

9

Ihr Code verhält, wie es funktioniert, weil Ihre Konstrukteure nicht virtuell sind. Das bedeutet, dass der Compiler zur Kompilierzeit an sie bindet. Das bedeutet also, dass der Laufzeittyp nicht berücksichtigt werden kann und der Code immer TParent.Create aufruft.

Damit das Programm mithilfe des Laufzeittyps binden kann, müssen Sie virtuelle Methoden und Polymorphie verwenden. So können Sie Ihr Problem durch die Verwendung virtueller Konstrukteure lösen:

program Project1; 

{$APPTYPE CONSOLE} 

type 
    TParent = class; 
    TParentClass = class of TParent; 

    TParent = class 
    public 
    ID: string; 
    constructor Create; virtual; 
    end; 

    TChild1 = class(TParent) 
    public 
    constructor Create; override; 
    end; 

    TChild2 = class(TParent) 
    public 
    constructor Create; override; 
    end; 

constructor TParent.Create; 
begin 
    ID := 'Parent'; 
end; 

constructor TChild1.Create; 
begin 
    ID := 'Child1'; 
end; 

constructor TChild2.Create; 
begin 
    ID := 'Child2'; 
end; 

procedure Test(ImplClass: TParentClass); 
var 
    ImplInstance: TParent; 
begin 
    ImplInstance := ImplClass.Create; 
    WriteLn(ImplInstance.ID); 
    ImplInstance.Free; 
end; 

begin 
    Test(TParent); 
    Test(TChild1); 
    Test(TChild2); 
    Readln; 
end. 

Ausgabe

 
Parent 
Child1 
Child2 

Eine Faustregel ist, dass, wann immer Sie verwenden, um eine Meta-Klasse ein Objekt, Ihre Klassen Konstruktor zu instanziiert sollte virtuell sein. Diese Faustregel hat Ausnahmen, aber ich persönlich musste diese Regel nie in meinem Produktionscode brechen.

+0

Vielen Dank für Ihre schnelle und hilfreiche Antwort! Könnten Sie helfen, mehr über diese Faustregel zu erklären? Müssen Konstruktoren virtuell sein, wenn sie nicht "explizit verwendet" werden? – SOUser

+0

Sehr gute Faustregel! Danke dir nochmal ! – SOUser