Es gibt zwei Arten wie eine Funktion mit gleichem namen eingeführt werden kann, shadowing und überschreiben.
Wenn du einfach eine Funktion mit selben Namen schreibst ist dies Shadowing, das heist so weil beide Funktionen immernoch existieren, du aber zugang zu der einen verdeckst mit der neuen. Der Compiler ruft immer die Funktion auf die er als erstes finden kann, also in diesem Fall:
Code: Alles auswählen
TFoo = class
procedure FooBar;
end;
TBar = class(TFoo)
procedure FooBar;
end;
...
var
x: TBar;
...
x.FooBar;
Hier schaut der Compiler nach und findet TBar.FooBar und führt diese aus. TFoo.FooBar ist somit im Schatten von TBar.FooBar und kann nicht gefunden werden, daher der begriff shadowing.
Du kannst aber immernoch TFoo.FooBar ausführen wenn du dem Compiler explizit sagst wo er suchen soll:
Die andere option ist überschreiben. Dabei existiert nur eine Referenz auf die Funktion, in der so genannten Virtual Method Table (VMT), welche überschrieben wird. Wenn dann die Funktion aufgerufen wird, wird in der VMT nachgeschaut welche Funktion eingetragen ist, und diese dann ausgeführt. Es gibt keine möglichkeit (die kein super unsauberer hack ist) die "alten" Funktionen direkt auszuführen, da sie ja überschrieben wurden.
Damit eine Funktion einen eintrag in der VMT bekommt muss diese als "virtual" markiert sein. Um eine funktion aus der VMT zu überschreiben, muss die neue Funktion als override markiert sein. Wenn die funktion nicht als override markiert ist, wird sie als nicht virtuelle funktion hinzugefügt und shadowed lediglich die virtuelle Funktion.
Code: Alles auswählen
TFoo = class
procedure FooBar; virtual;
end;
TBar = class
procedure FooBar; override;
end;
...
var
x: TBar;
...
x. FooBar; // ist das gleiche wie
TFoo(x).FooBar;
Egal wie die funktion aufgerufen wird, es wird immer die eine aus der VMT aufgerufen.
Nur noch der vollständigkeit halber, wenn eine funktion als virual und als abstract markiert ist, wird ein eintrag in die VMT hinzugefügt, aber dort steht keine funktion drin. Somit kann angegeben sein das es eine virtuelle Funktion geben soll, die aber zu diesem Zeitpunkt noch nicht bekannt ist, und einen Fehler werfen soll falls jemand versucht sie zu verwenden.
Das ding ist Konstruktoren werden meist von der Klasse selbst aufgerufen, wenn du ClassName.Create aufrufst ist hier kein Objekt mit VMT im Spiel, weshalb virtual konstruktoren in den meisten Fällen ziemlich Sinnfrei sind. Vor allem da auch ein gewisser Overhead mit der VMT verbunden ist.
Virtuelle Konstruktoren brauchst du nur wenn du diese über den Klassentypen aufrufen willst:
Code: Alles auswählen
TFoo = class
constructor CreateVirtual; virtual;
constructor CreateShadowing;
end;
TBar = class(TFoo)
constructor CreateVirtual; override;
constructor CreateShadowing;
end;
TFooClass = class of TFoo;
var
cls: TFooClass;
x, y: TFoo;
begin
cls := TBar;
x := cls.CreateVirtual; // ruft TBar.CreateVirtual auf und erstellt ein neues Objekt vom typen TBar
y := cls.CreateShadowing; // ruft TFoo.CreateShadowing auf (da cls vom typen class of TFoo ist) und erstellt ein neues Objekt vom typen TFoo
end;
Destruktoren hingegen sollten immer virtual sein damit man sie von egal wo in der Klassenhierachie freen kann. Um genau zu sein ruft .Free den Destructor von TObject auf, wenn dein Destruktor also nicht virtuell ist (also kein override hat), wird er bei .Free niemals aufgerufen.