DruckenMister WongFacebook

Object Pascal

von Martin Strohal, Andreas Hausladen, Christian Rehn

Objekte erzeugen: Konstruktor

Instanzen einer Klasse entstehen erst dadurch, dass sie ausdrücklich erzeugt werden. Dies geschieht mit dem Konstruktor Create. "Konstruktor" ist ein Fremdwort und bezeichnet jemanden, der etwas konstruiert, also erzeugt. Man könnte create auch eine Methode nennen; es gibt jedoch einen großen Unterschied: Methoden können nur aufgerufen werden, wenn die Instanz bereits existiert. Der Konstruktor dagegen erzeugt die Instanz aus dem Nichts.

Der Konstruktor muss nicht von uns programmiert werden, da alle Klassen, die wir erfinden, automatisch den "Vorfahr" TObject haben und damit alle grundlegenden Dinge von diesem erben. Weitere Informationen über TObject, den "Vater" aller Delphi-Objekte, finden sich in der Delphi-Hilfe. Unter .NET entspricht TObject der Klasse System.Object.

Und so wird eine Instanz erzeugt:

 Objektreferenz := Klasse.Create;

In unserem konkreten Fall also:

MeinAuto := TAuto.Create;

Damit wird Speicherplatz für alle Attribute einer Auto-Instanz im Hauptspeicher reserviert und die zugehörige Adresse in der Variablen MeinAuto gespeichert.

Beim Erzeugen einer Klasse werden die jeweils vorhandenen Attribute mit folgenden Startwerten belegt:

  • Alle Datenfelder mit einem ganzzahligen Datentyp (z.B. Integer) werden mit 0 initialisiert.
  • Alle Datenfelder mit einem String-Typ werden durch eine leere Zeichenkette initialisiert.
  • Alle Datenfelder mit einem Zeigertyp werden mit dem Wert nil initialisiert.
  • Alle anderen Datenfelder bleiben undefiniert!

Wie auf Attribute und Methoden der neu erzeugten Instanz zugegriffen wird, folgt unter "Zugriff auf Objekte".

create kann übrigens auch an einer Objektreferenz aufgerufen werden. Dadurch wird dann keine neue Instanz erzeugt, sondern es wird das Objekt neu initialisiert.

Anders als in C++ übernimmt der Konstruktor in Delphi neben der Initialisierung der Klasse auch die Aufgabe der Speicherreservierung. Der Vorfahrkonstruktor steht nun vor dem Problem, dass er wissen muss, dass die Speicherreservierung bereits durchgeführt wurde und somit kein weiterer Speicher zu reservieren ist. In Delphi wurde dies gelöst, indem der Konstruktor eine „Hybrid-Methode“ ist. Im Gegensatz zu normalen Methoden generiert der Compiler Code, der dem Konstruktor zwei versteckte Parameter übergibt. Der Typ des ersten Parameters „Self“ hängt dabei vom Wert des Zweiten ab. Dieser wird im CPU Register DL übergeben und gibt den Aufrufmodus des Konstruktors an. Delphi kennt drei unterschiedliche Aufrufmodi für Konstruktoren.

Der erste Aufrufmodus ist „ClassCreate with Allocation“ und wird beim Erzeugen einer Instanz der Klasse genutzt.

Reference := MyClass.Create;

Der Aufrufmodus-Parameter hat in diesem Fall den Wert 1. Dies führt dazu, dass als erstes die System-Funktion _ClassCreate, mit dem ClassType Self-Parameter, vom Konstruktor aus aufgerufen wird. Diese ruft ihrerseits die Klasssenmethode NewInstance auf, die den notwendigen Speicher reserviert und die Methode InitInstance aufruft. Diese wiederum initialisiert alle Felder des Objekts mit deren Null-Wert und baut die Interface Table auf. Zudem setzt sie den Zeiger auf die VMT (Virtual Method Table) in den dafür vorgesehenen Speicherbereich der Instanz ein. _ClassCreate richtet des Weiteren einen Exception-Block ein, der bei einer Exception innerhalb des Konstruktor-Codes automatisch den Destruktor aufgeruft. Dieser Exception-Block endet mit dem Verlassen des äußersten Konstruktors. Als Rückgabewert liefert_ClassCreate den mit NewInstance erzeugten Instanz-Zeiger, auch bekannt als Self, der nun im Konstruktor wie bei einer normalen Methode Verwendung findet.

Der zweite Aufrufmodus ist „Inherited Call“. Er dient dazu, den Vorfahrkonstruktor aufzurufen.

inherited Create;

Dieser darf natürlich keinen neuen Speicherplatz reservieren und auch die Klassen nicht neu initialisieren. Ein weiterer Exception-Block ist ebenfalls nicht notwendig. Damit bei dem „inherited“ Aufruf dies alles nicht geschieht, setzt der Compiler den Aufrufmodus-Parameter auf den Wert 0, was den Vorfahrkonstruktor veranlasst, den _ClassCreate Aufruf zu übergehen. Der Self-Parameter des Konstruktors ist in diesem Modus ein ganz gewöhnlicher Instanz-Zeiger, da das Objekt ja bereits erzeugt ist.

Der dritte Aufrufmodus ist „Initialization without Allocation“, bei dem zwar ein Exception-Block eingerichtet wird, aber keine Speicherreservierung stattfindet. Dieser Modus dient dem Aufruf des Konstruktors als gewöhnliche Methode.

Variable.Create;

Hierbei enthält der Aufrufmodus-Parameter den Wert -1 und Self ist ein Instanz-Zeiger. Dieser Aufrufmodus wird sehr selten eingesetzt, da es normalerweise nicht notwendig ist, den Konstruktor als reine Initialisierungsmethode zu verwenden, weil er bereits beim Erzeugen der Instanz ausgeführt wurde. Sie ist auf das TurboPascal object zurückzuführen, bei dem man, wie unter C++, den Speicher für dynamische Instanzen selbst reservieren musste. Zudem birgt diese Art des Aufrufs die Gefahr von Speicherlecks, die dadurch entstehen, dass bereits reservierter Speicher und Referenzen auf Objekte überschrieben und somit nicht mehr freigegeben werden können.

Bei allen drei Aufrufmodi wird nach dem Ausführen des Konstruktor-Codes die virtuelle Methode AfterConstruction ausgeführt.

Da dem Konstruktor zwei versteckte Parameter übergeben werden, welche die CPU-Register EAX (ClassType bzw. Self) und DH (Aufrufmodus) belegen, bleibt nur noch das ECX Register für einen dritten Parameter übrig. Der Rest muss über den langsameren Stack übergeben werden.