Habe ihr schon mal von der
VMT (virtual method table) gehört? Das ist das, was tommie-lie mit dem Eintrenken von Zeigern meint.
Eine Class besteht unter Delphi aus mindestens einem Feld, dem
VMT Zeiger. In dieser
VMT stehen nun alle Zeiger auf die virtuellen Methoden. Zudem gibt es in der
VMT ein Feld für die DMT (dynamic method table), in der eine Liste mit Index<->Methodenzeiger-Abbildungen enthalten ist und ein Zeiger auf die DMT der Oberklasse. Die
VMT ist für jede Klasse einmalig und wird bereits durch den Compiler komplett aufgebaut.
Die Klasse TObject kann man folgendermaßen umschreiben (pseudocode):
Delphi-Quellcode:
type
PVmt = ^TVmt;
TVmt =
array[-19..MaxWord]
of Pointer;
TObject = ^_TObject;
_TObject =
record
Vmt: PVmt;
end;
Wenn nun neue Felder hinzukommen, werden diese hinten angehängt.
Delphi-Quellcode:
type
TMyClass = class(TObject)
FMyField: Integer;
end;
Delphi-Quellcode:
type
TMyClass =
record
Vmt: PVmt;
FMyField: Integer;
end;
Wenn eine statische Methode aufgerufen wird, dann wird dieser zusätzlich ein unsichtbarer Parameter
Self übergeben, auf den sich dann der Code in der Methode bezieht. Bei virtuellen kann der Linker die Methoden-Adresse nicht einfach einsetzen. Somit muss die Adresse der Methode zur Laufzeit bestimmt werden. Hierzu wird die Methode über die
VMT aufgerufen, was ungefähr so in Pascal-Code aussehen würde:
Delphi-Quellcode:
var
MyInstance: TMyClass;
begin
...
// MyInstance.DoVirtual(10);
T_DoVirtual_MethodPtr( MyInstance.Vmt[VmtIndexOf(DoVirtual)] )(10);
...
end;
Bei dynamischen Methoden ist das noch ein wenig komplizierter, da dort nicht nur in der DMT der aktuellen Klasse gesucht werden muss, sondern, wenn dort kein passender Eintrag gefunden wurde, auch die DMTs der Oberklassen. Das sähe dann ungeführ so aus (pseudocode).
Delphi-Quellcode:
var
MyInstance: TMyClass;
// <temp>
Dmt: PDMT;
Idx: Integer;
// </temp>
begin
...
// MyInstance.DoDynamic(10);
Dmt := MyInstance.Vmt[System.vmtDynamicTable div SizeOf(Pointer)];
while (Dmt <> nil) do
begin
Idx := 0;
while (Dmt.Items[Idx].Index <> 0) do
begin
if Dmt.Items[Idx].Index = DmtIndexOf(DoDynamic) then
begin
T_DoDynamic_MethodPtr( Dmt.Items[Idx].Proc )(10);
Exit;
end;
Inc(Idx);
end;
Dmt := Dmt.ParentDmt;
end;
...
end;
Und weil das nicht wenig Code ist, hat Borland diesen Code in the Methode TObject.Dispatch ausgelagert. Zu BorlandPascal Zeiten gab es noch einen Cache für die zuletzt aufgerufene dynamische Methode um den Aufruf zu beschleunigen. In der Delphi-implementierung ist dieser nicht mehr vorhanden.