Analyse der Struktur und Zerstörung in Delphi
1 Objektmodell in Delphi: 2
1.1 Was bedeutet der Objektname? 2
1.2 Wo werden die Objekte gespeichert? 2
1.3 Was wird im Objekt gespeichert? Wie werden sie gespeichert?
2 Konstruktor und Erstellen von Objekt 5
2.1 Was ist ein Konstruktor? ("Spezielle" Klassenmethode) 5
2.2 Der gesamte Prozess des Erstellens eines Objekts 5
2.3 Alternative Verwendung von Konstruktoren (unter Verwendung von Klassenreferenzen zur Implementierung des Polymorphismus von Konstruktoren) 6
3 Zerstörer und Zerstörungsobjekt 7
3.1 Was ist ein Destruktor ("natürlich" virtuelle Methode) 7
3.2 Der gesamte Prozess der Objektzerstörung 7
3.3 Zerstören, frei, freeandnil, Veröffentlichung und Differenz 7
4 VCL Construction & Destructuring Architecture 8
5 Verwenden Sie Konstruktoren und Zerstörer korrekt 9
Analyse der Struktur und Zerstörung in Delphi
Zusammenfassung: Durch die Untersuchung von VCL/RTL analysiert dieses Papier den Implementierungsmechanismus von Konstruktoren und Zerstörern und die Architektur von Objekten in VCL und erklärt, wie man Objekte korrekt erstellt und freigibt.
Schlüsselwörter: Konstruieren, zerstören, Objekte erstellen, Objekte, Haufen, Stapel, Polymorphismus zerstören.
Autor: Majorsoft
Frage
Was ist der Implementierungsmechanismus von Konstruktoren und Zerstörern in Delphi? Wie erstellte und ließ ich Objekte korrekt erstellt und freigelassen?
Lösung
Wie man Konstruktion und Zerstörungen korrekt verwendet, ist ein häufiges Problem, das wir im Prozess der Verwendung von Delphi begegnen. Das Folgende ist, den Implementierungsmechanismus von Konstruktoren und Zerstörern durch die Untersuchung des VCL/RTL -Quellcode zu verstehen.
1 Objektmodell in Delphi:
1.1 Was bedeutet der Objektname?
Im Gegensatz zu C ++ repräsentiert der Objektname (auch eine Variable bezeichnet) in Delphi die Referenz eines Objekts und repräsentiert nicht das Objekt selbst, das einem Zeiger auf das Objekt entspricht, das als "Objektreferenzmodell" bezeichnet wird. Wie in der Abbildung gezeigt:
OBJ (Objektname) Das tatsächliche Objekt
VMT -Eingangsadresse
Datenmitglieder
1 Objektname bezieht sich auf ein Objekt im Speicher
1.2 Wo werden die Objekte gespeichert?
Jede Anwendung unterteilt den ihm zugewiesenen Speicher, um in vier Bereiche zu stoßen:
Codebereich
Globaler Datenbereich
Haufen
Stapelbereich
Abbildung 2 Programmspeicherraum
Codebereich: Programmcode im Programm, einschließlich aller Funktionscodes
Globaler Datenbereich: Speichert globale Daten.
Haufen Bereich: Auch als "freier Speicherbereich" bezeichnet, der dynamische Daten (einschließlich Objekte und Zeichenfolgen in Delphi) speichert. Umfang ist der gesamte Lebenszyklus der Anwendung, bis der Destruktor aufgerufen wird.
Stack Area: Auch als "automatischer Speicherbereich" bezeichnet, um lokale Daten in einem Programm zu speichern. Der Bereich befindet sich in der Funktion und das System recycelt sofort den Stapelraum nach dem Aufrufen der Funktion.
In C ++ können Objekte auf dem Haufen oder auf dem Stapel oder in den globalen Daten erstellt werden. In Delphi sind alle Objekte auf dem Haufenspeicherbereich aufgebaut, sodass der Delphi -Konstruktor nicht automatisch aufgerufen werden kann, sondern vom Programmierer selbst aufgerufen werden muss (die Komponente im Designer ziehen und das Objekt von Delphi erstellt werden). Das folgende Programm erläutert den Unterschied zwischen dem Erstellen von Objekten in Delphi und C ++:
In Delphi:
Prozedur createObject (var fooobjref: tfooObject);
Beginnen
Fooobjref: = tfooObject.create;
// vom Programmierer aufgerufen, nach dem Aufrufen des Prozesses besteht das Objekt immer noch.
FooObject.caption = 'Ich bin in Stack of createObject ()' erstellt;
Ende;
Und in C ++:
TfooObject CreateObject (void);
{
TfooObject fooObject; // lokale Objekte erstellen
// statische tfooObject fooObject; // statisches lokales Objekt erstellen
// Das Objekt wird automatisch erstellt, indem der Standardkonstruktor aufgerufen wird und das Objekt zu diesem Zeitpunkt im Funktionsstapel erstellt wird.
FooObject.caption = 'Ich bin in Stack of createObject ()' erstellt;
Fooobject zurückgeben;
// Das Objekt wird bei der Rückkehr kopiert, und das ursprüngliche erstellte Objekt wird automatisch zerstört, nachdem der Funktionsaufruf beendet ist}
TfooObject fooObject2; // Erstellen Sie globales Objekt.
void main ();
{TfooObject* pfooObject = new tfooObject;
// Erstellen Sie ein Heap -Objekt. Nachdem die Funktion aufgerufen wurde, existiert das Objekt immer noch und muss nicht kopiert werden. }
1.3 Was wird im Objekt gespeichert? Wie werden sie gespeichert?
Im Gegensatz zu C ++ speichern Objekte in Delphi nur die Eintragsadressen von Datenmitgliedern und virtuellen Methodentabellen (VMTs), speichern jedoch keine Methoden, wie in der Abbildung gezeigt:
Segment Virtual Method Table Codes -Code für Objekte
VMT -Adresse
Name: Zeichenfolge
Breite: Ganzzahl;
CH1: Char;
…
Proc1
Func1
…
Procn
funcn
…
Abbildung 3 Die Struktur des Objekts ...
Vielleicht haben Sie einige Fragen zur obigen Erklärung. Weitere Informationen finden Sie im folgenden Verfahren:
TSizeAnntest = Klasse
Privat
I: Ganzzahl;
CH1, CH2: Char;
J: Ganzzahl;
öffentlich
Prozedur Showmsg;
Prozedur virtuell;
Ende;
memo1.lines.add (InttoStr (SizetEst.instanceSize)+': Instanzen');
memo1.lines.add (inttoStr (Integer (Sizetest))+'<-start adDr');
memo1.lines.add (inttoStr (Integer (@(Sizetest.i))+'<-Sizetest.i');
memo1.lines.add (inttoStr (Integer (@(Sizetest.ch1)))+'<-Sizetest.ch1');
memo1.lines.add (inttoStr (Integer (@(Sizetest.ch2)))+'<-Sizetest.ch2');
memo1.lines.add (inttoStr (Integer (@(Sizetest.j))+'<-Sizetest.j');
Die Ergebnisse zeigen:
16: Instanzen
14630724 <-start addr
14630728 <-Sizetest.i
14630732 <-Sizetest.ch1
14630733 <-Sizetest.ch2
14630736 <-Sizetest.j
Das Datenmitglied und die VMT -Eintragsadresse belegen 16 Bytes!
Wo sind die Mitgliederfunktionen gespeichert? Da Delphi auf RTL (Laufzeitbibliothek) basiert, werden alle Mitgliedsfunktionen in der Klasse gespeichert. Wie finde ich also die Eintragsadresse der Mitgliedsfunktion? Für statische Funktionen erfolgt diese Arbeit vom Compiler. diesmal). muss zu diesem Zeitpunkt existieren.
Beachten
Wie oben erwähnt, werden alle Mitgliedsfunktionen in der Klasse gespeichert und enthalten tatsächlich die virtuelle Methode Tabelle VMT. Aus Delphis Code AutoComplete -Funktion (sie hängt von den Kompilierungsinformationen ab) können wir sehen, dass nach dem Eingeben des Objektnamens und der Eingabe "". Methoden, alle Konstrukteure und Zerstörer, können Sie es ausprobieren, wenn dies der Fall ist.
VMT -Eintragsadresse der virtuellen Methode Tabelle Klassen
Datenmitglied -Vorlageninformationen
Statische Methode Tabelle usw.
Virtuelle Methode Tabelle VMT
Objekt
VMT -Eingangsadresse
Datenmitglieder
Das obige Programm zeigt auch die Ausrichtung von Objektdatenmitgliedern (physikalische Datenstruktur), die in 4 Bytes (Windows -Standardausrichtung) ausgerichtet ist, wie in der folgenden Abbildung gezeigt:
VMT -Eingang addr
ich
CH1CH2
J
2 Konstruktor und Objekte erstellen
2.1 Was ist ein Konstruktor? ("Spezielle" Methode)
Aus der Semantik der OO-Idee (objektorientiert) ist der Konstruktor für die Erstellung des Objekts verantwortlich Das Aufrufen der internen Sub-Objekte) ist nicht für den gesamten Prozess des Erstellens von Objekten verantwortlich (siehe 2.2).
Darüber hinaus definiert Delphi im Gegensatz zu C ++ einen anderen Methodentyp für den Konstruktor (MkConstructor, siehe Zeile 125 im Delphi -Installationsverzeichnis), die wir als "spezielle" Klassenmethode verstehen können . Es kann nur durch die Klasse (Klassenname/Klassenreferenz/Klassenzeiger) aufgerufen werden, während allgemeine Klassenmethoden sowohl Klassen als auch Objekte aufgerufen werden können. und in den Klassenmethoden weist Selbst auf die Klasse hin, in der wir ihre Datenmitglieder normalerweise initialisieren, um sie zu einem echten Objekt zu machen, das alles dank des Selbstparameters ist.
Standardmäßig ist der Konstruktor eine statische Funktion. Erstellt mehrere Konstruktoren und kann den Konstruktor der übergeordneten Klasse in der abgeleiteten Klasse auch direkt überlagern. von Struktur & Zerstörungen (siehe 4)
2.2 Der gesamte Prozess der Objekterstellung
Der vollständige Erstellen eines Objekts sollte das Zuweisung von Raum, das Erstellen physischer Datenstrukturen, das Initialisieren und Erstellen interner Sub-Objekte umfassen. Wie oben erwähnt, ist der Konstruktor nur für die Initialisierung und den Konstruktor der internen Sub-Objekte verantwortlich. Dies liegt daran, dass der Compiler zusätzliche Dinge tut, wir wissen es nicht. Wenn Sie an den Konstruktor kompilieren, wird vor dem Bau der Funktion eine Zeile "CALL @CLASSCREATE" eingefügt. :
Funktion _classCreate (Aclass: tclass; alloc: boolean): tobject;
Asm
{ -> eax = zeiger auf vmt}
{<- eax = pointer auf Instanz}
…
Rufen Sie DWORD PTR [EAX] an .vmtnewinstance // newinstance rufen
…
Ende;
Vmtnewinstance = -12;
Klassenfunktion NewInstance: Tobject;
Klassenfunktion Tobject.NewInstance: Tobject;
Beginnen
Ergebnis: = initinstance (_getMem (Instances));
Ende;
"Initinstance (_getMem (Instances))", ruft drei Funktionen nacheinander auf:
1) Erste Anrufinstanzen (), um die Objektgröße der tatsächlichen Klasse zurückzugeben
Klassenfunktion Tobject.instance: Longint;
Beginnen
Ergebnis: = Pinteger (Integer (Selbst) + VMtinStancesize)^; // Geben Sie die Objektgröße der tatsächlichen Klasse zurück
Ende;
2) Rufen Sie _getMem () auf, um den Speicher von Instanzgröße auf dem Heap zuzuordnen und die Referenz des Objekts zurückzugeben
3) Rufen Sie initinstance () auf, um die physikalische Datenstruktur zu konstruieren und den Standardwert des Mitglieds festzulegen, z. Wenn es eine virtuelle Methode gibt, weisen Sie den ersten vier Bytes des Objekts die Eintragsadresse der virtuellen Methode -Tabelle VMT zu.
Nach dem Aufrufen von Newinstance hat das Objekt zu diesem Zeitpunkt nur eine "leere Hülle" und keinen tatsächlichen "Inhalt". Daher ist es erforderlich, einen individuellen Konstruktor aufzurufen Objekte im Programm, um Objekte in der realen Welt wirklich zu reflektieren. Dies ist der gesamte Prozess der Objekterstellung.
2.3 Alternative Verwendung von Konstruktoren (unter Verwendung von Klassenreferenzen zur Implementierung des Polymorphismus von Konstruktoren)
In Delphi werden auch Klassen als Objekte gespeichert, so dass es auch Polymorphismus mit Klassenreferenzen und virtuellen Klassenmethoden implementiert wird, die eine Polymorphismus-Implementierung auf Klassenebene liefert. Legen Sie die Klassenmethode als virtuelle Methode fest, überschreiben Sie sie in der abgeleiteten Klasse und rufen Sie sie dann über den Referenz/den Zeiger der Basisklasse auf, so dass das Objekt basierend auf der Klassenreferenz/dem Zeiger auf die tatsächliche Klasse konstruiert wird. Bitte beachten Sie das folgende Programm:
Tmyclass = class
Konstruktor erstellen; virtuell;
Ende;
TtmyClass = Klasse von tmyclass; // Klassenreferenz der Basisklasse
TmyClassSub = Klasse (tmyClass)
Konstruktor erstellen;
Ende;
Prozedur createObj (ACLASS: TTMYCASS; var ref);
Beginnen
Tobject (ref): = aclass.create;
// Ref ist keiner Art und ist mit irgendeiner Art nicht kompatibel, daher muss es bei der Verwendung explizit gegossen werden (Guss)
// ACLASS ist eine Klassenreferenz, eine einheitliche Funktionsschnittstelle und verschiedene Implementierungen.
// Es konstruiert Objekte basierend auf der tatsächlichen Klasse, auf die ACLASS verwiesen wurde.
Ende;
…
CreateObj (tmyClass, OBJ);
CreateObj (tmyclassSub, subobj);
3 Zerstörer und zerstören Objekte
3.1 Was ist ein Destruktor ("natürlich" virtuelle Methode)
Semantisch gesehen ist der Destruktor dafür verantwortlich, Objekte zu zerstören und Ressourcen freizusetzen. In Delphi, synonym.
Delphi definiert auch einen Methodentyp für den Destruktor (MkConstructor, siehe Line /Source/Rtl/Common/Typinfo.pas, 125 im Delphi -Installationsverzeichnis). ; virtuell; "in allen Vorfahren der VCL -Klasse - Tobjekt. Warum macht VCL das? Weil es sicherstellt, dass das Objekt in polymorphen Situationen korrekt zerstört werden kann. Wenn keine virtuellen Methoden angewendet werden, dürfen Sie das Unterobjekt der Basisklasse nur zerstören, was zu dem sogenannten "Speicherleck" führt. Um den richtigen Destruktor zu gewährleisten, muss der Destruktor eine Überschreibungsdeklaration hinzufügen.
3.2 Der gesamte Prozess der Objektzerstörung
Zerstören Sie zuerst das abgeleitete Klassenunterobjekt und zerstören Sie dann das Unterobjekt der Basisklasse.
Hinweis
In einer abgeleiteten Klasse bezieht sich das Unterobjekt der Basisklasse auf den Teil der Basisklasse, und das untergeordnete Objekt in der abgeleiteten Klasse bezieht sich auf den neu hinzugefügten Teil.
3.3 Zerstören, frei, freeandnil, Freisetzungsnutzung und Unterschiede
Zerstörung: Virtuelle Methode
Speichern Sie den Speicher frei, deklarieren Sie es als virtuell in Tobject, überschreiben Sie ihn normalerweise in seiner Unterklasse und fügen Sie das ererbte Schlüsselwort hinzu, um sicherzustellen, dass das abgeleitete Klassenobjekt korrekt zerstört wird.
Aber Destroy kann nicht direkt verwendet werden, warum?
Wenn ein Objekt NIL ist, nennen wir immer noch Zerstörungen, ein Fehler tritt auf. Da Destroy eine virtuelle Methode ist, muss sie die Eingangsadresse der virtuellen Methode Tabelle VMT basierend auf den ersten vier Bytes im Objekt finden, um die Eingangsadresse von Zerstörungen zu finden, damit das Objekt zu diesem Zeitpunkt existieren muss. Aber frei ist eine statische Methode. Die Verwendung von freier Verwendung ist sicherer als die Verwendung von Zerstörungen.
2) Frei: Statische Methode
Testen Sie, ob das Objekt Null ist, und die Zerstörung wird aufgerufen, wenn es nicht Null ist. Hier ist der Delphi -Code kostenlos:
procedure tobject.free;
Beginnen
Wenn Selbst <> nil dann
Zerstören;
Ende;
Ist es nicht großartig, aus den Stärken und Schwächen zu lernen?
Das Aufrufen von Zerstörungen zerstört jedoch nur das Objekt, setzt jedoch nicht den Verweis des Objekts auf NIL, das vom Programmierer durchgeführt werden muss.
3) Freeandnil;
Freeandnil Definition in Sysutils Einheit
Verfahren Freeandnil (var obj);
var
Temp: tobject;
Beginnen
Temp: = tobject (obj);
Zeiger (obj): = nil;
Temp.free;
Ende;
Wir empfehlen, dass Sie es anstelle von frei/zerstören verwenden, um sicherzustellen, dass das Objekt korrekt freigegeben wird.
4) in TCustomform definierte statische Methode.
Die freie Funktion wird aufgerufen, nachdem alle Ereignisse im Fenster verarbeitet wurden. Es wird oft verwendet, um Fenster zu zerstören, und wenn die Ereignisverarbeitung in diesem Fenster eine gewisse Zeit in Anspruch nimmt, kann diese Methode sicherstellen, dass das Fenster erst nach dem Verarbeiten des Fensters zerstört wird. Hier ist der Delphi -Quellcode von tcustomForm.Release:
Verfahren tcustomForm.Release;
Beginnen
Postmessage (Handle, CM_RELEASE, 0, 0);
// CM_Release -Nachricht an das Fenster an die Meldungswarteschlange senden.
// Rufen Sie CM_Release Message Processing Process CMrelease erneut auf
Ende;
Schauen wir uns die folgende Definition von CMrelease für die Verarbeitung von CM_Release -Nachrichten an:
Prozedur cmrelease (var message: tmessage);
Verfahren tcustomForm.cmrelease;
Beginnen
Frei; // am Ende ist es frei;
Ende;
4 VCL Construction & Destructuring Architecture
Tobjekt
Konstruktor erstellen; // statische Methode
Zerstörer zerstören;
Tpersistent
Zerstörer zerstören;
Tkomponent
Konstruktor create (Aowner: tcomponent);
Zerstörer zerstören;
Tcontrol
Konstruktor create (aowner: tcomponent);
Zerstörer zerstören;
…
Die folgenden Analysen des Quellcode des Konstruktion und der Zerstörung in VCL, wobei TCONTROL als Beispiel eingenommen wird:
Konstruktor tcontrol.create (aowner: tcomponent);
Beginnen
Geerbte Create (Aowner); // Erstellen Sie ein Unterobjekt der Basisklasse und übergeben Sie die Zerstörungsrechte an Aowner. Stelle es vor
// Dies stellt sicher, dass die Reihenfolge "Erstellen von Basisklassen -SuboDject und dann abgeleitete Klassenunterobjekte" erstellen ".
… // Initialisieren und nennen Sie den Konstruktor des internen Untertreffers
Ende;
Destructor Tcontrol.Destroy;
Beginnen
… // interne Unterobjekte in abgeleiteten Klassen zerstören
erbte zerstören; // das Basisklassenobjekt zerstören und es am Ende setzen
// Dies sorgt für die Reihenfolge der "ersten Zerstörung abgeleiteter Klassen -Unterobjekte, dann Zerstörung von Unterobjekten der Basisklasse" "
Ende;
5 Konstrukteure und Zerstörer richtig verwenden
Nach der obigen Analyse fasst im Folgenden die Prinzipien der Verwendung von Konstruktoren und Zerstörern zusammen:
Bevor Sie ein Objekt verwenden, müssen Sie zunächst ein Objekt erstellen und das Objekt rechtzeitig zerstören, um Ressourcen freizugeben.
Wenn sich zwei Objekte auf Zuordnungen beziehen, stellen Sie sicher, dass das namenlose Objekt (das sich auf Objekte bezieht, auf die nicht verwiesen wird), auf die erscheinen kann.
Beim Erstellen einer Komponente wird empfohlen, eine Hostkomponente einzurichten (dh den Parameter Aowner, normalerweise ein Formular), und Aowner verwaltet die Zerstörung von Objekten, sodass sich die Komponente nicht Sorgen machen muss Delphi für das Design des Formulars/Datenmoduls und das Erstellen von Komponenten ist die Methode. Wir müssen also den Destruktor, der die Komponente nennt, nicht schreiben.
Wenn der Rückgabetyp der Funktion ein Objekt ist, ist das Ergebnis auch ein Verweis auf das Objekt, um sicherzustellen, dass das von Ergebnis verwiesene Objekt existieren muss.
Um obj <> nil oder zugewiesen (NIL) zu testen, dass das Objekt existiert, sollte OBJ: = NIL auch nach OBJ: = NIL aufgerufen werden.
Bitte beachten Sie den Quellcode des Demo -Programms
Anweisungen (empfohlen)
Alle Delphi -Programme wurden an Win2K+Delphi6 SP2 weitergegeben. Um Ihr Verständnis dieses Artikels zu vertiefen, wird empfohlen, sich auf das Demonstrationsprogramm zu verweisen.
Dieser Artikel enthält einige meiner Erfahrungen und Erfahrungen im Lernen von VCL/RTL.
Vor dem Lesen dieses Artikels müssen die Leser ein gewisses Verständnis der orientierten Pascal -Sprache haben und das Polymorphismus verstehen können, wenn Sie einige dieser Konzepte nicht sehr klar sind, bitte beziehen Sie sich auf die entsprechenden Artikel.
In diesem Artikel sollten Sie in der Lage sein, den Implementierungsmechanismus von Objektmodell, Konstruktion und Zerstörungen in Delphi und die Konstruktions- und Zerstörungsarchitektur in VCL zu verstehen und die Verwendung von Bau- und Zerstörungsmethoden zu beherrschen. Die Struktur und Zerstörung in Delphi sind viel einfacher als die in C ++, und wir sollten in der Lage sein, sie zu beherrschen.