Einzelnen Beitrag anzeigen

Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#8

Re: Methodenhierarchie in der Vererbung umgehen/überspringen

  Alt 12. Jan 2004, 14:56
@Choose: die .CommonMethod() in meinem Beispiel zeigt wie es OOP konform zu lösen ist.

@scouty: Es sind virtuelle/Dynamische Methoden und exakt weil deine TypCast Methode nicht funktionieren kann, habe ich dir die beiden Wege aufgezeigt.

Nun, warum kann ein TypCast nicht funktionieren ?
Jedes allozierte Object hat intern einen Zeiger auf seine Klasse. Eine Klasse ist nichts anderes als eine Speicherstruktur die im Codesegement gespeichert wurde. D.h. werden zB. 100 Objecte vom Typ TMyObject erzeugt so zeigen alle diese 100 Objecte auf ein und dieselbe Klassendefinition ins Codesegement. In dieser Klassendefinition sind zB. der ClassName, ParentClass, ClassType, die VMT = Virtual Method Table, die DMT = Dynamic Method Table usw. usw. gespeichert. Wird nun eine Virtuelle Methode aufgerufen so wird aus der Klassendefinition in der VMT die realle Addresse der Methode die aufgerufen werden soll ausgelesen. Der Code verzeigt nach diesem Auslesen also an diese Methodenadresse. Nun da Self.Classtype = TC ist wird somit immer auf die VMT von TC zugegriffen. In dieser VMT steht ein Zeiger auf @TC.Method und nicht TA.Method. Somit wird auch bei einem TypCast trotzdem TC.Method aufgerufen. So und nicht anders SOLLEN virtuelle methoden auch funktionieren.

Um aber wiederum zu tricken, müssen wir also den Klassentyp von Self= TC in den Klassentyp TA ändern. Nachdem dies passiert ist würde man mit TC(Self).Method() nun tatsächlich TA.Method aufrufen. Dies geht dann so:

Delphi-Quellcode:
procedure TC.Method(Dummy: Integer);
type
  PPointer = ^Pointer;
var
  N: procedure(Dummy: Integer) of object;
begin
  inherited;
  ShowMessage('TC');

// nun rufen wir inherited TA.Method(Dummy) auf
  TMethod(N).Data := Self;
  TMethod(N).Code := @TA.Method;

  N(Dummy);

// nun so wie es auf herkömmliche OOP Weise funktioniert
  CommonMethod(Dummy);

// hier wird die Klasse TC von Self^ in Klasse TA gepatcht, und wieder zurück
  ShowMessage('Klassenname ist ' + ClassName);
  PPointer(Self)^ := TA;
  ShowMessage('Klassenname ist ' + ClassName);
  Method(Dummy);
  PPointer(Self)^ := TC;
end;
Im letzten Code siehtst du also das der erste Zeiger im Speicher von Self^ ein Zeiger auf den Klassentyp von Self ist. Jedes Object speichert also als erstes unsichtbares Feld seinen eigenen Klassentyp. Der Klassentyp selber ist nichts anderes als ein Zeiger in das Codesegment wo der Compiler die Klassendefinition abgelegt hat. Biegen wir diesen Zeiger um so können wir den Klasentyp eines beliebigen Objectes auf einen anderen Klassentyp patchen. Aber Vorsicht dies hat gravierende Konsequenzen.
In unserem Fall hat dies zu Konsequenz das nun die VMT=Virtuelle Methoden Tabelle von Self nicht mehr die originale von TC ist sondern die von TA. Somit wird der nachfolgende Aufruf von Self.Method() nicht wie erwartet eine Rekursion hervorufen, sondern eben TA.Method() aufrufen.

Gruß Hagen
  Mit Zitat antworten Zitat