Einzelnen Beitrag anzeigen

jbg

Registriert seit: 12. Jun 2002
3.483 Beiträge
 
Delphi 10.1 Berlin Professional
 
#4

Re: Wie wird OOP Compiler intern realisiert?

  Alt 20. Mär 2004, 14:35
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.
  Mit Zitat antworten Zitat