Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Konstruktor Create Virtual oder nicht? (https://www.delphipraxis.net/98594-konstruktor-create-virtual-oder-nicht.html)

smudo 29. Aug 2007 13:44


Konstruktor Create Virtual oder nicht?
 
Ich steh hier momentan so'n bisschen auf dem Schlauch und wollte deshalb mal meine Frage in die Runde werfen.

Ist es notwendig bzw. besser, einen Konstruktor als virtual zu deklarieren, um diesen in abgeleiteten Klassen überschreiben zu können?
Delphi-Quellcode:
classA = class(TPersistent)
  constructor Create;
end;

classB = class(ClassA)
  constructor Create;
end;
Verwende ich in den Creates des inherited, dann funtioniert das genauso, wie diese Variante:
Delphi-Quellcode:
classA = class(TPersistent)
  constructor Create; Virtual;
end;

classB = class(ClassA)
  constructor Create; Override;
end;
Was ist richtiger?

Danke
René

DeddyH 29. Aug 2007 13:52

Re: Konstruktor Create Virtual oder nicht?
 
Zitat:

Zitat von Delphi-Hilfe
Mit Hilfe der Direktiven virtual und dynamic können Methoden als virtuell oder dynamisch deklariert werden. Virtuelle und dynamische Methoden können im Gegensatz zu statischen Methoden in abgeleiteten Klassen überschrieben werden. Beim Aufrufen einer überschriebenen Methode bestimmt nicht der deklarierte, sondern der aktuelle Typ (also der Typ zur Laufzeit) der im Aufruf verwendeten Klassen- bzw. Objektvariable, welche Implementierung aktiviert wird.
Um eine Methode zu überschreiben, braucht sie nur mit der Direktiven override erneut deklariert zu werden. Dabei müssen Reihenfolge und Typ der Parameter sowie der Typ des Rückgabewertes (falls vorhanden) mit der Deklaration in der Vorfahrklasse übereinstimmen.

Willst Du also auch den Konstruktor der Vaterklasse aufrufen (inherited), dann ist virtual die bessere Wahl.

smudo 29. Aug 2007 13:54

Re: Konstruktor Create Virtual oder nicht?
 
Aber es geht doch auch ohne, wenn ich inherited verwende. Warum - was ist der Unterschied?

Apollonius 29. Aug 2007 13:55

Re: Konstruktor Create Virtual oder nicht?
 
Meiner Meinung nach brauchst du virtual bei Konstruktoren und Klassenmethoden nur, wenn du mit Klassenreferenztypen (Metaklassen) arbeitest, oder den Konstruktor an einer Instanz aufrufst (Instanz.create;), was ich bisher noch nie gemacht habe.

DeddyH 29. Aug 2007 14:04

Re: Konstruktor Create Virtual oder nicht?
 
Hier ist das recht gut erklärt (zumindest besser, als ich das könnte):https://www.delphi-treff.de/object-p...en-ueberladen/

Apollonius 29. Aug 2007 14:07

Re: Konstruktor Create Virtual oder nicht?
 
Ja, aber hier reden wir über Konstruktoren, und da ist ein virtual vollkommen sinnlos, wenn man keine Metaklassen verwendet (oder sie an einer Instanz aufruft, s. o.).

smudo 29. Aug 2007 14:09

Re: Konstruktor Create Virtual oder nicht?
 
Ich muss mal nachfragen: Was meinst du mit MetaKlasse?

Apollonius 29. Aug 2007 14:13

Re: Konstruktor Create Virtual oder nicht?
 
Schau mal in die Delphi-Hilfe. Code sagt mehr als tausend Worte:
Delphi-Quellcode:
type TMetaclass=class of TMyClass; //Deklaration des Metaklassentyps
var metaklasse: TMetaclass;
begin
metaklasse:=TMyclass;
metaklasse:=TMyDerivedClass;
metaklasse:=TObject; //geht nicht, da nicht von TMyClass abgeleitet
metaklasse.statischeMethode;
end.
Nur für solche Situationen brauchst du virtuelle Klassenmethoden, denn sonst rufst du sie ja direkt an der Klasse auf, sodass keine späte Bindung nötig ist.

smudo 29. Aug 2007 14:32

Re: Konstruktor Create Virtual oder nicht?
 
Nach dem genauen Lesen des ersten Beitrages von Appolonius bin ich jetzt wieder runter vom Schlauch. Danke.

Muetze1 29. Aug 2007 15:24

Re: Konstruktor Create Virtual oder nicht?
 
Zitat:

Zitat von Apollonius
Meiner Meinung nach brauchst du virtual bei Konstruktoren und Klassenmethoden nur, wenn du mit Klassenreferenztypen (Metaklassen) arbeitest, oder den Konstruktor an einer Instanz aufrufst (Instanz.create;), was ich bisher noch nie gemacht habe.

Nein, die brauchen keinen virtuellen Konstruktor, die gehen auch mit komplett nicht virtuellen.

PMM 29. Aug 2007 15:41

Re: Konstruktor Create Virtual oder nicht?
 
Vieleicht hilft folgendes weiter: Der Konstruktor wird ja aufgerufen, bevor das Objekt existiert- das soll ja gerade erzeugt werden. Es gibt also noch gar kein Instanz die bestimmen könnte, welche evtl. überschriebene Methode aufzurufen ist. Bei allen anderen Methoden ist das anders: da gibt es ja schon ein Objekt das bestimmen kann, welche konkrete Methode zur Laufzeit gemeint ist.
PMM

negaH 29. Aug 2007 16:10

Re: Konstruktor Create Virtual oder nicht?
 
Zitat:

Der Konstruktor wird ja aufgerufen, bevor das Objekt existiert-
Falsch der Konstruktor wird aufgerufen nachdem das Objekt auf dem Speicher erzeugt und initaisiert wurde damit man in seinem eigenen Konstruktor diese Instance noch weiter initialisieren kann. Ist der Konstruktor dabei virtuell und override deklariert wurden dann kann man mit Hilfe eines dynamischen Klassentypes der Vorfahrklasse in einer Variable auch eine Instance der abgeleiteten Klasse als Instance erzeugen. Deshalb machen virtuelle Konstruktoren auch einen Sinn.

Delphi-Quellcode:
type
  TBaseClass = class(TObject)
    constructor Create(const BaseClassParameter); virtual;
  end;

  TBaseClassClass = class of TBaseClass;

  TSecondClass = class(TBaseClass)
    constructor Create(const BaseClassParameter); override;
  end;

procedure Test;
var
  BaseClass: TBaseClassClass;
begin
  BaseClass := TBaseClass;
  with BaseClass.Create(Params) do Free;

  ShowMesssage(BaseClass.ClassName);


  BaseClass := TSecondClass;
  with BaseClass.Create(Params) do Free;
 
  ShowMesssage(BaseClass.ClassName);
end;
Eine Variable BaseClass enthält den Klassetyp den man dynamisch zu Laufzeit erzeugen möchte. Die Regeln der Klassevererbung herrschen also auch bei Klassenvariablen und Klassenmethoden. Beim Konstruktor ist nun wichtig das er virtuell ist damit man unabhängig von der realen Klasse in der Variablen BaseClass auch dynamisch eine Instanz erzeugen kann.

Manche bezeichnen das als Metaklassen. Wichtig ist nur zu wissen das es ohne diese Feature, also auch der Möglichkeit von virtuellen Konstruktoren in diesem Zusammenhang die Streaming-Fähigkeiten der VCL garnicht umsetzbar wären. Alsodas Speihcern und Laden von Komponenten samt TForm in einen Stream als DFM Resource/Datei. Auch die TGraphic Objecte würden als Ganzes nicht die gewohnte Funktionalität bieten.

Die Möglichkeit für virtuelle Konstruktoren sind also ein MUSS !

Nochwas zur Erstellungsreiehenfolge von Instancen:

1.) .InstanceSize wird aufgerufen um die Größe des Speichers des Objektes zu ermitteln
2.) .InitInstance wrd aufgerufen um kompexe Datenstrukturen die als Felder im Objekt deklariert wurden zu initlaisieren
3.) erst jetzt wird der Konstruktor aufgerufen der seinerseit eventuell inherited Konstruktor aufruft
4.) .AfterConstruction wird aufgerufen

Gruß Hagen

Muetze1 29. Aug 2007 16:44

Re: Konstruktor Create Virtual oder nicht?
 
Zitat:

Zitat von PMM
Vieleicht hilft folgendes weiter: Der Konstruktor wird ja aufgerufen, bevor das Objekt existiert- das soll ja gerade erzeugt werden. Es gibt also noch gar kein Instanz die bestimmen könnte, welche evtl. überschriebene Methode aufzurufen ist.

Dadurch dass er die überschriebenen Constructoren etc aus der VMT ermittelt kann er dies auch ohne Instanz, schliesslich ist diese in den Klasseninformationen abgelegt, welche er mit dem Klassennamen (bzw. das was in den von negaH erklärten Metaklassen Variablen enthalten ist (ein Zeiger auf diese Information)) bekommt und somit nachschauen kann.

Sehe ich das so richig, negaH? Ich frage in letzter Zeit lieber nochmal nach...

negaH 29. Aug 2007 17:57

Re: Konstruktor Create Virtual oder nicht?
 
Jain. In der Klassenstruktur steht zwar ein Zeiger auf den aktuell implementierenden Konstruktor der Klasse, Konstruktoren sind damit per se immer virtuell ;) Das steht nicht in der VMT -> virtual Method Table, diese ist nur einer der Bestandteile einer heutigen Klassenstruktur. Siehe System.pas.

aber !

Wir möchten dynamisch zur Laufzeit erst die zu erzeugende Klassse definieren, wie im Beispiel ob TBaseClass und/oder TSecondClass. Damit man beiden Klassen auf diese Art&Weise erzeugen kann und auch Paramater dieser Klassen übergeben kann muß man nit explizit virtuell deklarierten Klassen arbeiten.

Macht man die nicht so würde im 2. Aufruf in der BaseClass den Klassentyp TSecondClass enthält nicht der Constructor von TSecondClass aufgerufen sondern derjenjge Konstruktor der über

type
TBaseCLass = class of TBaseClass

deklariert wurde. Im zweiten Aufruf würde man also Konstruktor TBaseClass.Create aufrufen aber eine Instance vom Typ TSecondClass erhalten ! Hat man nun im Konstruktor von TSecondClass eigene individuelle Implementierungen vorgesehen so würden diese eben nicht ausgeführt werden. Man erzeugt eine Instance vom Typ TSecondClass ruft aber TBaseClass.Create auf.

Probierts einfach aus. In den obigen 2 Klasen im Konstruktor eine ShowMessage(ClassName) rein. Dann als 1. den Konstruktor nicht virtual deklarieren und den Test Code von oben starten. Es muß 2 mal "TBaseClass" angezeigt werden obwohl wir einmal eine Instance von TBaseClass und einmal von TSecondClass erzeugt haben. Dann alles virtuell machen. Nun geht so wir gewünscht.

Metaklassen können meistens nur dann sinnvoll benutzt werden wenn deren Konstruktor als virtual deklariert wurde. So wie bei TComponent.Create(AOwner: TComponent);

Metaklassen, ich mag diese Wort nicht für diese Art der OOP. Es gibt defakto keine Unterscheidung zwischen diesen Metaklassen, also der Benutzung von typisierten Klassenvariablen -> type class of Class, zu der Benutzung vin Klassen im Allgemeinen. Die Deklaration/Benutzung von Klassen ist genauso OOP wie die Deklaration/Benutzung von typsisierten Klassen-Variablen oder von typisierten Instanzen einer Klasse, kurz Objekten. Alles sind Features der OOP, Metaklassen suggeriert was falsches und ist eine Marketingschpfung, selbst wenn sich nur ein schlauer Programmierer damit besser verkaufen möchte.

Metaklassen sind nichts anderes als Klassenvariablen um dynamisch den Typ der zu erzeugenden Instanzeklasse festlegen zu können.
Der Typ so einer Klassevariablen ist immer ein

Delphi-Quellcode:
type class of Class;
Diese Deklaraton suggeriert das man einen Klasentyp zu einer Klassenhierarchie als Überbau deklariert, sprich eine Metaklasse. Das ist Quatsch. Man deklariert einen neuen Datentyp vom Typ Pointer der aber typisiert nur Klassen aufnehmen kann die von de Basis Klasse abgleietet wurden. Wenn es eine Metaklasse wäre dann könnte man mit TBaseClassClass.Create() auch eine Meta-Instance also Meta-Objekt dieser Metaklasse erzeugen.

Es sind typsierte Klassenvariablen und definitiv eine Grundlage für die Polymorphie der Klassen. Es ist Polymorph af Klassenebene weil in der Variablen "BaseClass"

1.) nur typisierte und von der Mutterklasse TBaseClass abgeleitete Klassen gespeichert werden können. Der Compiler wird einen Fehler bringen wenn man was anders versucht.
2.) alle dynamisch zur Laufzeit veränderlich ist, also immer ein anderes Gesicht annehmen kann, ja nach Klassentyp den man in dieser Variable speichert
3.) eben Zeiger auf eine Klasse, und das ist ein hardcoded Recordstruktur im Codesegement die eine Klasse beschreibt, speichert.

Gruß Hagen

[edit]ähm sorry für meine Tippfehler, das trainiert eure Intelligenz, seht's positiv ;) [/edit]

Apollonius 29. Aug 2007 21:25

Re: Konstruktor Create Virtual oder nicht?
 
So, jetzt muss ich auch noch mal meinen Senf dazu geben.
Zitat:

In der Klassenstruktur steht zwar ein Zeiger auf den aktuell implementierenden Konstruktor der Klasse, Konstruktoren sind damit per se immer virtuell
Das verstehe ich gerade überhaupt nicht. Wieso sind Konstruktoren "automatisch" virtuell? Ich habe mich mal ziemlich weit in die VMT eingegraben, einen Zeiger auf den aktuell implementierenden Konstruktor habe ich nicht gefunden...

Zitat:

ShowMessage(ClassName) rein. Dann als 1. den Konstruktor nicht virtual deklarieren und den Test Code von oben starten. Es muß 2 mal "TBaseClass" angezeigt werden obwohl wir einmal eine Instance von TBaseClass und einmal von TSecondClass erzeugt haben.
:wiejetzt: Du hast doch eben erklärt, dass wir eine TSecondclass-Instanz erstellt haben, folglich zeigt ClassName auch TSecondclass an, da Classname auf der VMT beruht, die nun mal TSecondclass ist. Wir würden 'TBaseClass' erhalten, wenn wir ein showmessage mit dem hartcodierten Klassennamen in beiden Konstruktoren seperat aufrufen würden.

Zitat:

Metaklassen können meistens nur dann sinnvoll benutzt werden wenn deren Konstruktor als virtual deklariert wurde. So wie bei TComponent.Create(AOwner: TComponent);
Nur der Vollständigkeit halber: Mit Klassenmethoden geht's natürlich auch.

Zitat:

Metaklassen, ich mag diese Wort nicht für diese Art der OOP.
Deshalb wird das ganze in der Delphi-Hilfe auch "Klassenreferenztyp" genannt. Im Gegensatz zu anderen Programmierklasse handelt es sich in Delphi ja nicht um eine Klasse, weshalb auch ich diesen Begriff treffender finde.
Das ist auch etwas, was Delphi hat, C++ jedoch nicht. In C++ wäre es ein rein logischer Widerspruch, da dort Klassenmethoden als "static" deklariert werden, und das beißt sich mit "virtual".

Genug geschreibselt. :coder: Ich hoffe, ich habe nicht nur Müll erzählt.
Gute Nacht
Apollonius

negaH 29. Aug 2007 22:26

Re: Konstruktor Create Virtual oder nicht?
 
Zitat:

Das verstehe ich gerade überhaupt nicht. Wieso sind Konstruktoren "automatisch" virtuell? Ich habe mich mal ziemlich weit in die VMT eingegraben, einen Zeiger auf den aktuell implementierenden Konstruktor habe ich nicht gefunden...
Ok, was unterscheidet primär eine statische Methode von einer virtuellen/dynamsichen Methode ?

Die statische Methode muß nicht überschrieben werden, sie exitiert statisch für alle Klassen der nachfolgenden Hierarchie der abgeleiteten Klassen und es ist immer die gleiche Methode. Dadurch besteht nicht die Notwendigkeit in der Klassenstruktur einen Zeiger zu reservieren der individuell für die nachfolgenden Klassen veränderbar sein muß. Statische Methoden tauchen also in der Klassenstruktur/record im Codesegment garnicht auf.

Der Zeiger auf den Konstruktor einer Klasse findet sich aber in der Klassenstruktur, quasi als "Datenfeld". Somit kann eine abgeleitete Klasse diesen Zeiger auf eine andere Methode umdefinieren. Das ist virtuel und nicht statisch.
Da in den Delphi Klassen ein solcher Slot existiert für den Konstruktor wie auch Destructor sind diese per se virtuelle Methoden, auch wenn wir sie nicht explizit als virtuell deklarieren.

Deklarieren wir eine virtuell Methode in einer Klasse so wird in der dazugehörigen Klassenstruktur in deren VMT=virtual Method Table am Ende dieser geerbten und vom Vorgänger 1 zu 1 kopierten VMT diese neue Klassenbezogene VMT um einen neuen Slot für diese neue virtuelle Methode reserviert. Darin steht der Zeiger auf unsere Methode. Die VMT einer Klassenhierarchie kann also immer nur additiv arbeiten, logisch da sich der Index einer einmal in einer Vorgängerklasse deklarierten virtuellen Methode in dieser VMT nie verändern darf. Nur so kann man sicherstellen das innerhalb einer überschriebenen virtuellen Methode das inherited auch funktioniert. Inherited kennt den Index/Offset der eigenen Methoden und schaut nun in der Parent.Class.VMT am gleichen Index nach, lädt den Zeiger der Methode und führt einen CALL dorthin durch. Das geht in der vorherigen überschriebenen Methoden mit inherited immer weiter bist zur Rootklasse die als erste diese virtuelle Methode deklariert hat. Ab diesem Moment ist die VMT der Parent Classe in ihrer Größe/Anzahl an VMT Slot kleiner. Dh. diese Vorgänger klasse enthlt diese in der Nachfolgeklasse deklarierte virtuelle Methode nicht in ihrer VMT.

Das bedeutet:

- VMT einer Klasse ist ein Array[] of Procedure
- eine Klasse erbt 1 zu 1 als Kopie die VMT ihrer Vorgängerklasse
- eine Klasse die neue virtuelle Methoden deklariert hängt diese neuen Slots an diese Kopie hinten dran
- damit wird der Speicherverbrauch im Codesegemt für diese Klassenstrukturen immer größer je mehr Klassen und mehr virtuelle Methoden man deklariert
- der Aufrufoverhead für virtuelle Methoden, inklusive des inherited Aufrufs, beschränkt sich auf einen indirekten CALL [Class.VMT.Index]
- die aktive Mitarbeit des Compilers ist von nöten. Man kann also nicht direkt zur Lauzeit die VMT benutzen um per Index eine virtuelle Methode aufzurufen. Dafür gibt es Compiler Makros VMTOffset() o.ä.
- in der VMT wird nicht hinterlegt wieviele Slots in der VMT gespeichert sind. Man kann also nicht direkt über die Klassenstruktur ermitteln wieviele virtuelle Methoden eine Klasse besitzt. Das geht aber über Umwege denoch ;) Einfach mal in den Borland.Newsgroups nach meinem Posting mit VMTSlotOf() suchen.

Eine Instance einer Klasse enthält als ersten Zeiger in seiner Datenstruktur einen Zeiger direkt in die Klassenstruktur, also in die Mitte derselben. An dieser Addresse steht im Codesegement der VMT. Danach folgt die DMT = dynamic Method table.
Das hat zwei wichtige Gründe

1.) historisch bedingt. Die alten TObjecte aus Borland Pascal zeiten besaßen eine Klassenstruktur die direkt mit der VMT began. Damit die alten Objekte zu den neuen Klassen halbwegs kompatibel blieben hat man wohl diesen Weg gewählt.
2.) man kann so sehr schnell ausgehend von einem Instanzzeiger indirekt durch dessen Klassen.VMT eine virtuelle Methode anspringen

Nun die neue Klassenstruktur besitzt aber vor ihrer VMT, also mit negativem Offset noch weitere Felder. In diesen findest du auch die Einträge für den Konstruktor und Destruktor. Diese sind per se virtuell.

In System.pas findest du diese Konstanten

vmtSelfPtr = -76;
vmtIntfTable = -72;
vmtAutoTable = -68;
vmtInitTable = -64;
vmtTypeInfo = -60;
vmtFieldTable = -56;
vmtMethodTable = -52;
vmtDynamicTable = -48;
vmtClassName = -44;
vmtInstanceSize = -40;
vmtParent = -36;
vmtSafeCallException = -32;
vmtAfterConstruction = -28;
vmtBeforeDestruction = -24;
vmtDispatch = -20;
vmtDefaultHandler = -16;
vmtNewInstance = -12;
vmtFreeInstance = -8;
vmtDestroy = -4;

vmtQueryInterface = 0;
vmtAddRef = 4;
vmtRelease = 8;
vmtCreateObject = 12;

Nun eine Klassenstruktur sieht so aus

Delphi-Quellcode:
Class = packed record
 vmtSelfPtr          = -76; // hm;) ein Sanitycheck, zeigt auf @VMT was gleichbedutend mit dem Klassenzeiger ist
  vmtIntfTable        = -72; // Zeiger auf eine Tabelle in der die implementierten Interfaces dieser Klasse stehen
  vmtAutoTable        = -68; //
  vmtInitTable        = -64; // Zeiger auf Init Tabelle in dieser Struktur, dort steht welche Felder einer Instance autom. initialisert werden müssen und eben auch wie
  vmtTypeInfo         = -60; // Zeiger auf die RTTI=Run Time Type Information dieser Klasse
  vmtFieldTable       = -56;
  vmtMethodTable      = -52;
  vmtDynamicTable     = -48; // Zeiger auf @DMT
  vmtClassName        = -44; // Zeiger auf den Klassennamen, folgt der DMT in dieser Struktur
  vmtInstanceSize     = -40; // größe einer Instance dieser Klasse im Speicher
  vmtParent           = -36; // SelfClass.ClassParent Vorfahrklasse
 
  vmtSafeCallException = -32; // ab hier alles Zeiger auf Methoden
  vmtAfterConstruction = -28;
  vmtBeforeDestruction = -24;
  vmtDispatch         = -20;
  vmtDefaultHandler   = -16;
  vmtNewInstance      = -12;
  vmtFreeInstance     = -8;
  vmtDestroy          = -4; // zb. hier der Destruktor einer Klasse
 
 
  VMT: array[0..3] of procedure; // vmtQueryInterface, vmtAddRef, vmtRelease, vmtCreateObject <- Constructor !!
  DMT: packed record
         EntryCount: Word;
         Entries: array[EntryCount] of packed record
           SlotID: Word;
           Method: procedure;
         end;          
       end;

  ClassName: ShortString;
  InterfaceTable: ....
... blabla
end;
ein Object zeigt nun direkt in diese Struktur an @VMT, PPointer(Instance)^ = Instance.ClassType = @Instance.ClassType.VMT


Wenn wir also zb. mit Pointer(TComponent)^ in das Codesegement zugreifen so finden wir dort @TComponent.VMT aus obiger Struktur.

Eine Klasse ist also ein Record im Codesegemt desjenigen Modules in das diejenige Unit eingelinkt wurde in der diese Klasse deklariert wurde. Das erklärt auch warum Packages funktionieren und warum die vielen Beispiele von TForms in DLL ohne Packages zu benutzen garnicht funktonieren können. Denn zb. der "as" oder "is" Operatior macht nichts anderes als Zeiger auf Klassen im Codesegemtn zu vergleichen.

Nun dieses Pamphlet könnte ich garantiert noch um das 4 fache verlängern, zb. was sind dynamsiche Methoden, was haben sie mit message methoden gemein, wie arbeiten diese, wie ist die Methodtable = publsiched Events und methoden aufgebaut, wie kann man aus dieser Tabelle alle Parameter deren Datentypen usw. ermitteln. Was macht die Interface Table, wir wird ein Interface-Zeiger einer Interface-Instance beim Aufruf einer ihrer Methoden in eine Object-Instance umgeserzt, als wie kommt man von einem Interface-Zeiger zu einem Object-Self-Zeiger damit man eine TObject als Basis für die Implementierung der Interfaces benutzen kann. Was steht so alles in der RTTI einer Klasse.

Wenn man nun noch die Historie kennt, also zb. seit Borland pascal 5 die Weiterentwicklung gerade dieser Internas mitverfolgen durfte so kann man

1.) sich ein Urteil über die abnehmende Qualität der Entwickler bei Borland in der nähren Vergangenheit erlauben
2.) man kann sehr gut erkennen wie mit jedem neuen Feature -> Delphi-Klassen -> Interfaces -> DispIntfs -> dyn. Message Methoden -> bishin zur RTTI, nach und nach in dieses Konzept eingebaut wurden.

Ich fands bisher spannend, naja in den letzten Jahren nicht mehr so sehr da sich ja nichts gravierendes mehr getan hat.

Gruß Hagen

PS: sorry für die Tippfehler, im dunkeln auf der Laptoptastur, sollte eure Intelligenz anregen mein Kauderwelsch.

negaH 29. Aug 2007 22:31

Re: Konstruktor Create Virtual oder nicht?
 
Zitat:

u hast doch eben erklärt, dass wir eine TSecondclass-Instanz erstellt haben, folglich zeigt ClassName auch TSecondclass an, da Classname auf der VMT beruht, die nun mal TSecondclass ist. Wir würden 'TBaseClass' erhalten, wenn wir ein showmessage mit dem hartcodierten Klassennamen in beiden Konstruktoren seperat aufrufen würden.
Probier meinen Vorschlag aus und dann....
Der Zugriff auf den Klassennamen innerhalb eines aufgerufenen Konstrukturs ruft dynamsich immer den Klassennamen auf der zur aktuellen Klasse in der ich mich befinde zugehörig ist.

Wenn also BaseClass.Create() intern SchoMessage(ClassName) aufruft dann zeigt das den Klassennamen der Klasse auch an. Wenn wir in BaseClass nun TSecondClass drinen stehen haben der Konstruktor aber NICHT virtuell deklariert wurde dann wird defakto eben nich TSecondClass.Create() aufgerufen sonder TBaseClass.Create(). das ist auch logisch da der Datentyp der Variablen BaseClass: class of TBaseClass ist.

Lerne was Polymorphie heist und warum man bei virtuellen Methoden/Konstrukoren eben BaseClass.Create() aufrufen kann und der richtige Konstruktor nur dann aufgerufen wird wenn dieser als virtuel deklariert wurde.

Probiere es erstmal aus.

Was bestimmt denn bei einem Object zu welcher Klasse es gehört ? Probiert das aus

Delphi-Quellcode:
type
  PPointer = ^Pointer;
var
  Obj: TObjct;
begin
  Obj := TObject.Create;
  ShowMessage(Obj.ClassName);

  PPointer(Obj)^ := @TComponent;
 
  ShowMessage(Obj.ClassName);
end;

// oder das

type
  TMyClass = class(TObject)
    procedure Test;
  end;

procedure TMyClass.Test;
begin
  ShowMessage(ClassName);
end;

procedure Test;
var
  T: TMyClass;
begin
  T := TMyClass.Create;
  T.Test;

  PPointer(T)^ := @TPanel;

  T.Test;
end;
Gruß hagen

Sidorion 30. Aug 2007 08:21

Re: Konstruktor Create Virtual oder nicht?
 
Ähhh verwechselt mal bitte nicht das Schlüsselwort 'static' mit statischen Methoden. 'static' entspricht 'class' vor einer Funktion/Methode in Delphi und bedeutet nur, dass man diese Methode ohne Instanz rufen kann (geht bissi über Class Function hinaus, aber das führt hier zu weit). Insofern können in C++ 'static' Methoden durchaus überschrieben werden (genauso wie class functions). 'static' und 'abstract' hingegen machen wirklich keinen Sinn :)

Muetze1 30. Aug 2007 08:56

Re: Konstruktor Create Virtual oder nicht?
 
Zitat:

Zitat von Sidorion
Insofern können in C++ 'static' Methoden durchaus überschrieben werden (genauso wie class functions).

Verdeckt, aber nicht überschrieben. Ausser du deklarierst sie als virtuell...

Nochwas: class procedure die als virtuell und abstract markiert werden, werden in Delphi ordentlich gehandelt (samt Überschreiben etc). Ich hatte dieses Verhalten angewandt um bei meiner XMLLib die StringAdapter zu bauen. Hintergrund: Ich wollte vor anlegen der Instanz wissen, welche Zeichenkodierung die Klasse implementiert. Da ich nur einen Klassenreferenztyp hatte, musste es eine virtuelle Methode sein um den zu bekommen. Da es aber auch noch ohne Instanz vorher aufgerufen werden musste, wurde es zu einer virtuellen class procedure. Nächster Schritt: Jeder der einen solchen StringAdapter baut muss mir mindestens diesen Coding String zurück geben - somit abstract. Dieses Verhalten klappte soweit, bis ich versuchte die Klasse im C++Builder zu übersetzen. Dort wollte der Compiler diesen Konstrukt nicht mehr übersetzen (obwohl es doch noch immer der Delphi Compiler war) - ich vermute da Delphi dort etwas stark eigenes baut, was dann der C++Builder/Compiler nicht mehr nutzen kann, wenn ich von der Klasse mit einem solchen Konstrukt ableiten würde.

Ich habe nun alles ohne Zwang zu überschreiben und das klappt einwandfrei. Also nur das abstract entfernt.

Sidorion 30. Aug 2007 09:27

Re: Konstruktor Create Virtual oder nicht?
 
Hmmmm ich hab jetzt doch mal mein Wischelstudio angeschmissen, um mal genau nachzuforschen: static kann nicht zusammen mit Vererbung benutzt werden (kein virtual, override oder abstract). Insofern isses gegenüber Delphi eingeschränkter, allerdings kann ich in C++ auch Variablen static machen, was bei Rekursionen und so extrem lecker sein kann. Hier heisst das dann das diese Variable nur einmal im Programm existiert (allerdings mit den entsprechenden Scopierungen, wie Klasse, Funktion usw.).
[edit]insofern sind 'static' Methoden auch statische Methoden, umgekehrt aber nicht zwingend (da fehlt dann einfach das virtual wie in Delphi)[/edit]

Muetze1 30. Aug 2007 10:23

Re: Konstruktor Create Virtual oder nicht?
 
Zitat:

Zitat von Sidorion
Insofern isses gegenüber Delphi eingeschränkter, allerdings kann ich in C++ auch Variablen static machen, was bei Rekursionen und so extrem lecker sein kann. Hier heisst das dann das diese Variable nur einmal im Programm existiert (allerdings mit den entsprechenden Scopierungen, wie Klasse, Funktion usw.).

Das klappt in Delphi auch: Lokale Konstante mit Typ definieren und in den Projekt-Optionen die veränderbare Konstanten zulassen. Das ist dann auch eine einwandfreie statische Variable.

In den neuen Delphi Versionen (afair: ab D2005) gibt es gleichfalls static Variablen - auch hier wird der Präfix "class" verwendet.

Zitat:

Zitat von BDS2006 Hilfe
Mithilfe einer class var-Deklaration können Sie einen Block von statischen Klassenfeldern innerhalb einer Klassendeklaration erzeugen. Alle nach class var deklarierten Felder haben statische Speicherattribute. Ein class var-Block wird durch Folgendes abgeschlossen:
  • Eine weitere class var-Deklaration
  • Eine Prozedur- oder Funktionsdeklaration (Methodendeklaration) (einschließlich Klassenprozeduren und Klassenfunktionen)
  • Eine Eigenschaftsdeklaration (einschließlich Klasseneigenschaften)
  • Eine Konstruktor- oder Destruktor-Deklaration
  • Ein Sichtbarkeitsattribut (public, private, protected, published, strict private und strict protected)
Ein Beispiel:
Delphi-Quellcode:
 type
   TMyClass = class
     strict private
       class var        // Felder müssen als Klassenfelder deklariert werden
          FRed: Integer;
          FGreen: Integer;
          FBlue: Integer;
       public            // Ende des class var-Blocks
          class property Red: Integer read FRed write FRed;
          class property Green: Integer read FGreen write FGreen;
          class property Blue: Integer read FBlue write FBlue;
   end;
Auf die obigen Klasseneigenschaften kann folgendermaßen zugegriffen werden:
Delphi-Quellcode:
TMyClass.Red := 0;
TMyClass.Blue := 0;
TMyClass.Green := 0;


Sidorion 30. Aug 2007 10:29

Re: Konstruktor Create Virtual oder nicht?
 
Dass der Schalter bereits ab Delphi4 (oder früher hab kein D1-D3) als veraltet gebrandmarkt und Konstanten keine Variablen sind (auch wenn man sie verändern kann) stört Dich dabei nicht? Naja meine Meinung über Compilerschaltermagie habe ich hier im Forum schon zu genüge geäußert. Dass es sowas in D2006 gibt, find ich allerdings schick.

Muetze1 30. Aug 2007 11:20

Re: Konstruktor Create Virtual oder nicht?
 
Zitat:

Zitat von Sidorion
Dass der Schalter bereits ab Delphi4 (oder früher hab kein D1-D3) als veraltet gebrandmarkt und Konstanten keine Variablen sind (auch wenn man sie verändern kann) stört Dich dabei nicht?

Wie sollte ich das Problem aber sonst lösen unter einem solchen Delphi wo ich die neuen Sprachfeatures noch nicht zur Verfügung habe? Den Auftrag ablehnen?

Sidorion 30. Aug 2007 11:34

Re: Konstruktor Create Virtual oder nicht?
 
Ganz ehrlich, ich bin noch nieeeee in die Verlegenheit gekommen, diesen Schalter bedienen zu müssen. Entweder reicht eine globale Variable mit dem entsprechenden Zuriffsschutz (Singelton, CriticalSection) oder das Design kann geändert werden, wobei der Zugriffsschutz müsste auch bei einer Konstante realisiert werden.

Muetze1 30. Aug 2007 13:36

Re: Konstruktor Create Virtual oder nicht?
 
Zitat:

Zitat von Sidorion
Entweder reicht eine globale Variable ...

Global nicht, auch nicht Unit-Global. Somit gibt es keine entsprechendes Pendant zur Lösung.

Apollonius 30. Aug 2007 15:15

Re: Konstruktor Create Virtual oder nicht?
 
Hagen, Hagen... mir musst du das mit Polymorphie, dynamisch, virtuell und und und nicht erklären...
Und ich habe es eben nochmal ausprobiert: man kriegt TSecondClass raus.
Ein Aufruf von TBaseclass.create kriegt als allerersten Parameter die Klassenreferenz mit, die in diesem Fall TSecondclass ist, und den Konstruktor haben wir beauftragt, Klassenreferenz.ClassName anzuzeigen.
WARUM IN ALLER WELT SOLLTE JETZT TBASECLASS ANGEZEIGT WERDEN? Wir haben ausdrücklich TSecondClass als Klassenreferenz übergeben, und dieser Name wird angezeigt.
Mein Testcode:
Delphi-Quellcode:
program Project2;

{$APPTYPE CONSOLE}

uses
  SysUtils;
type
TMClass=class
  constructor create;
end;
TAClass=class(TMClass)
end;
TMClassClass=class of TMClass;

constructor TMClass.create;
begin
writeln(ClassName);
end;

var MClass:TMClassClass; Instanz:TMClass;
begin
MClass:=TAClass;
Instanz:=MClass.create;
Instanz.free;
readln;
end.
Zurück zum Konstruktorzeiger:
Zitat:

Delphi-Quellcode:
VMT: array[0..3] of procedure; // vmtQueryInterface, vmtAddRef, vmtRelease, vmtCreateObject <- Constructor !!

Das ist doch ein Auszug aus einer Interface-VMT? Meine Interface-Methoden beginnen bei $0C, also direkt nach QueryInterface ($00), _Addref ($04) und _Release ($08). Wäre natürlich theoretisch möglich, dass sich das mal geändert hat (ich habe Delphi 2005 Personal, also leider auch ohne Sourcecode).
[edit] Er kennt WMTCreateObject zwar, aber alle vier Interface-VMT-Konstanten sind, im Gegensatz zu den Klassen-VMT-Konstanten, als veraltet markiert.[/edit]

Dieses konstruktive Streitgespräch beginnt, mir Spaß zu machen...
Gruß
Apollonius

negaH 30. Aug 2007 20:23

Re: Konstruktor Create Virtual oder nicht?
 
Stop mal, ich kann mich auch selber austricksen.

Wo ist deine TAClass.Create ?

Also erstmal noch eine TAClass Create anlegen. In TMClass Create nun ShowMessage('1'); und in TAClass Create ShowMessage('2';

Nun MClass := TAClass; und MClass.Create;

Was zeigt er bei dir an ? '1' oder '2' ?

Nach diesem Test TMClass.Create als virtual und TAClass.Create als override; Und dann nochmal schauen was ShowMessage sagt.
Und wenn ich am Montag von meiner Dienstreise zurück bin, dann diksutieren wir nochmal was ich oben gesagt habe, ok ;)

Metaklassen -> bedingt virtuelle Konstrukoren -> damit man die Klassen individuell initialisieren kann.

Gruß Hagen

Apollonius 30. Aug 2007 20:53

Re: Konstruktor Create Virtual oder nicht?
 
Ich glaube, dass bedeutet, dass wir die ganze Zeit aneinander vorbeigeredet haben: Ich meinte, dass TBaseclass.create mit der Klassenreferenz TSecondclass aufgerufen wird, und du sagtest, dass TBaseclass.create aufgerufen wird. Das widerspricht sich wohl nicht.
Ich dachte, dass du in deinen Konstruktor ebenfalls den Classname (deshalb meine Ergänzung mit hartcodierten Namen) anzeigen ließest.

Zitat:


Wenn also BaseClass.Create() intern SchoMessage(ClassName) aufruft dann zeigt das den Klassennamen der Klasse auch an. Wenn wir in BaseClass nun TSecondClass drinen stehen haben der Konstruktor aber NICHT virtuell deklariert wurde dann wird defakto eben nich TSecondClass.Create() aufgerufen sonder TBaseClass.Create(). das ist auch logisch da der Datentyp der Variablen BaseClass: class of TBaseClass ist.
Das habe ich wohl ziemlich falsch interpretiert.

Aber zurück zu den statischen Konstruktoren - in der VMT oder nicht, das ist hier die Frage. Kannst du mir die Bedeutung von VMTCreateObject erklären, denn bei meinen eigenen Nachforschungen bin ich nicht darauf gestoßen?
Apollonius

Apollonius 1. Sep 2007 13:20

Re: Konstruktor Create Virtual oder nicht?
 
Ich würde schon gerne die Bedeutung von VMTCreateObject wissen. Deshalb hole ich diesen Thread mal wieder hoch.

Apollonius 2. Sep 2007 15:01

Re: Konstruktor Create Virtual oder nicht?
 
Noch ein Versuch: Was bedeutet VMTCreateObject?

MatthiasR 23. Sep 2008 08:04

Re: Konstruktor Create Virtual oder nicht?
 
Da meine Frage grob in die gleiche Richtung geht und ich nicht extra für so etwas banales nen neuen Thread aufmachen will, häng ich mich einfach mal hinten dran.

Ich möchte lediglich wissen: wenn ich eine eigene Klasse von TObject ableite mit einem eigenen Konstruktor und Destruktor, muss ich dann zu Beginn des Konstruktors erstmal inherited aufrufen, genauso wie am Ende des Destruktors? Also quasi so:
Delphi-Quellcode:
interface

  TMyClass = class(TObject)
  private
    FBlaBlubb: TBlaBlubb;
  public
    constructor Create;
    destructor Destroy; override;
    property BlaBlubb: TBlaBlubb read FBlaBlubb write FBlaBlubb;
  end;

implementation

{ TMyClass }

constructor TMyClass.Create;
begin
  inherited;

  FBlaBlubb := nil;
end;

destructor TMyClass.Destroy;
begin
  FBlaBlubb.Free;

  inherited;
end;
Oder sind beide inherited-Aufrufe unnötig oder gar falsch? Oder nur einer von beiden?!?

Roachford 23. Sep 2008 08:46

Re: Konstruktor Create Virtual oder nicht?
 
Da die Destruktoren und Konstruktoren in TObject leer sind, benötigst du es nicht unbedingt. Aber damit hast du auch wieder einen möglichen Fehler geschaffen: Wenn du später mal zwischen TObject und deiner Klasse eine weitere Basisklasse dazwischenschiebst, dann werden deren Konstruktoren- und Destruktorenanweisungen nicht ausgeführt. Und das suchste denn ein paar Wochen nach der Deklaration der ersten Klasse vergeblich, warum das nicht klappt.

Der Speicher für die Instanz wird eh vor dem Konstruktoraufruf alloziiert, somit brauchst du nicht zu fürchten, dass die Instanz nicht komplett ist ohne den inherited Aufruf.

Im Endeffekt bist du mit dem inherited Aufruf auf der sicheren Seite.

BTW: Deine Initialisierung im Konstruktor ist nicht zwingend notwendig, da Member automatisch mit dem Ordinalwert 0 initialisiert werden, also nil, false, 0.0, 0, #0 und '' (wobei Strings eine kleine Ausnahme bilden).

MatthiasR 23. Sep 2008 14:41

Re: Konstruktor Create Virtual oder nicht?
 
Zitat:

Zitat von Roachford
Der Speicher für die Instanz wird eh vor dem Konstruktoraufruf alloziiert, somit brauchst du nicht zu fürchten, dass die Instanz nicht komplett ist ohne den inherited Aufruf.

Danke, dass du das nochmal explizit geschrieben hast, da war ich mir nämlich nicht so ganz sicher, wie sich das verhält.
Zitat:

Zitat von Roachford
Im Endeffekt bist du mit dem inherited Aufruf auf der sicheren Seite.

Alles klar, dann werde ich das doch so beibehalten :)

Vielen Dank für deine Hilfe!


Alle Zeitangaben in WEZ +1. Es ist jetzt 10:14 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz