En train d'utiliser DELPHI pour développer des logiciels, nous sommes comme un groupe de vaches et de moutons heureux dans la prairie, profitant insouciante du soleil que nous apporte le langage Pascal Objet et des riches plantes aquatiques fournies par divers contrôles VCL. En regardant le ciel bleu sans limites, en regardant l'herbe verte luxuriante de la terre, qui penserait à la taille de l'univers et aux choses qui sont plus petites que les molécules et les atomes ? C'est une affaire de philosophes. A cette époque, le philosophe était assis au sommet d'une haute montagne, regardant les changements dans les nébuleuses de l'univers, fixant les insectes rampants sur le sol, se retournant soudainement, hochant la tête et souriant à notre groupe de pâturages. bovins et moutons. Il ramassa un morceau d'herbe, le tint doucement dans sa bouche, ferma les yeux et le goûta soigneusement. Je me demande quel était le goût de ce morceau d'herbe dans la bouche du philosophe ? Cependant, il avait toujours un sourire satisfait sur le visage.
Connaître et comprendre le monde atomique microscopique de DELPHI peut nous permettre de comprendre en profondeur la structure d'application macroscopique de DELPHI, développant ainsi notre logiciel dans un espace idéologique plus large. C'est comme si Newton avait découvert le mouvement des objets macroscopiques, mais qu'il était troublé parce qu'il ne parvenait pas à comprendre pourquoi les objets bougeaient ainsi. Au contraire, Einstein a vécu la vie heureuse de la relativité entre les lois des particules fondamentales et le mouvement des objets macroscopiques. !
Section 1 TObject Atome
Qu'est-ce que TObject ?
C'est le noyau de base de l'architecture du langage Pascal Objet et l'origine de divers contrôles VCL. Nous pouvons considérer TObject comme l'un des atomes qui composent une application DELPHI. Bien entendu, ils sont constitués de particules plus subtiles telles que des éléments de base de la syntaxe Pascal.
On dit que TObject est l'atome du programme DELPHI car TObject est pris en charge en interne par le compilateur DELPHI. Toutes les classes d'objets sont dérivées de TObject, même si vous ne spécifiez pas TObject comme classe ancêtre. TObject est défini dans l'unité System, qui fait partie du système. Au début de l'unité System.pas, il y a ce texte de commentaire :
{Constantes, types, procédures prédéfinies, }
{ et fonctions (telles que True, Integer ou }
{Writeln) n'a pas de déclarations réelles.}
{ Au lieu de cela, ils sont intégrés au compilateur }
{ et sont traités comme s'ils étaient déclarés }
{ au début de l'unité système.}
Cela signifie que cette unité contient des constantes, des types, des procédures et des fonctions prédéfinis (tels que : True, Integer ou Writeln). Ils ne sont pas réellement déclarés, mais sont intégrés par le compilateur et sont utilisés au début de la compilation. être une définition énoncée. Vous pouvez ajouter d'autres fichiers de programme source tels que Classes.pas ou Windows.pas à votre fichier de projet pour compiler et déboguer le code source, mais vous ne pouvez absolument pas ajouter le fichier de programme source System.pas à votre fichier de projet pour la compilation ! DELPHI signalera les erreurs de compilation pour les définitions en double de System !
Par conséquent, TObject est une définition fournie en interne par le compilateur. Pour ceux d'entre nous qui utilisent DELPHI pour développer des programmes, TObject est une chose atomique.
La définition de TObject dans l'unité System est la suivante :
TObject = classe
constructeur Créer ;
procédure Gratuit;
fonction de classe InitInstance (Instance : Pointeur) : TObject ;
procédure CleanupInstance ;
fonction ClassType : TClass ;
fonction de classe ClassName : ShortString ;
fonction de classe ClassNameIs (const Name : string) : Booléen ;
fonction de classe ClassParent : TClass ;
fonction de classe ClassInfo : pointeur ;
fonction de classe InstanceSize : Longint ;
fonction de classe InheritsFrom(AClass: TClass): Boolean;
fonction de classe MethodAddress (const Name : ShortString) : Pointeur ;
fonction de classe MethodName (Adresse : Pointeur) : ShortString ;
function FieldAddress (const Name : ShortString) : pointeur ;
fonction GetInterface (const IID : TGUID ; out Obj) : Booléen ;
fonction de classe GetInterfaceEntry(const IID : TGUID) : PInterfaceEntry ;
fonction de classe GetInterfaceTable : PInterfaceTable ;
fonction SafeCallException (ExceptObject: TObject;
ExceptAddr : Pointeur ): HResult ; virtuel ;
procédure AfterConstruction; virtuel;
procédure AvantDestruction ; virtuelle ;
procédure Dispatch(var Message);
procédure DefaultHandler(var Message);
fonction de classe NewInstance : TObject virtuel ;
procédure FreeInstance ; virtuelle ;
destructeur Détruire; virtuel;
fin;
Ensuite, nous frapperons progressivement à la porte des atomes de TObject pour voir quelle structure se trouve à l’intérieur.
Nous savons que TObject est la classe de base de tous les objets, alors qu'est-ce qu'un objet exactement ?
Tout objet dans DELPHI est un pointeur, qui indique l'espace occupé par l'objet en mémoire ! Bien que l'objet soit un pointeur, lorsque nous faisons référence aux membres de l'objet, nous n'avons pas besoin d'écrire le code MyObject^.GetName, mais nous pouvons uniquement écrire MyObject.GetName. Il s'agit d'une syntaxe étendue du langage Pascal Objet. pris en charge par le compilateur. Les amis qui utilisent C++ Builder sont très clairs sur la relation entre les objets et les pointeurs, car les objets dans C++ Builder doivent être définis comme des pointeurs. L'endroit pointé par le pointeur d'objet est l'espace objet où l'objet stocke les données. Analysons la structure des données de l'espace mémoire pointé par le pointeur d'objet.
Les 4 premiers octets de l'espace objet pointent vers la table d'adresses de méthode virtuelle (VMT?C Virtual Method Table) de la classe d'objet. L'espace suivant est l'espace pour stocker les données membres de l'objet lui-même, et est stocké dans l'ordre total depuis les données membres de la classe ancêtre la plus primitive de l'objet jusqu'aux données membres de la classe d'objet, et dans l'ordre dans lequel les les données membres sont définies dans chaque niveau de classe.
La table de méthodes virtuelles (VMT) d'une classe contient les adresses de procédure des méthodes virtuelles de toutes les classes dérivées de la classe ancêtre d'origine de la classe. La méthode virtuelle d'une classe est une méthode déclarée avec le mot réservé virtual. La méthode virtuelle est le mécanisme de base pour réaliser le polymorphisme des objets. Bien que les méthodes dynamiques déclarées avec le mot réservé dynamique puissent également réaliser le polymorphisme d'objet, ces méthodes ne sont pas stockées dans la table d'adresses de méthode virtuelle (VMT). Il s'agit simplement d'une autre méthode fournie par Object Pascal qui peut économiser de l'espace de stockage de classe. mais au détriment de la vitesse d'appel.
Même si nous ne définissons nous-mêmes aucune méthode virtuelle de classe, l'objet de la classe a toujours un pointeur vers la table d'adresses de méthode virtuelle, mais la longueur de l'entrée d'adresse est nulle. Cependant, où sont stockées les méthodes virtuelles définies dans TObject, telles que Destroy, FreeInstance, etc. ? Il s'avère que leurs adresses de méthode sont stockées dans un espace décalé dans le sens négatif par rapport au pointeur VMT. En fait, l'espace de données décalé de 76 octets dans le sens négatif de la table VMT correspond à la structure de données système de la classe d'objets. Ces structures de données sont liées au compilateur et pourront être modifiées dans les futures versions de DELPHI.
Par conséquent, vous pouvez penser que VMT est une structure de données commençant à partir de l'espace d'adressage de décalage négatif. La zone de données de décalage négatif est la zone de données système de VMT, et les données de décalage positif de VMT sont la zone de données utilisateur (méthode virtuelle personnalisée). table d'adresses). Les fonctions et procédures liées aux informations de classe ou aux informations d'exécution d'objet définies dans TObject sont généralement liées aux données système de VMT.
Une donnée VMT représente une classe. En fait, VMT est une classe ! Dans Object Pascal, nous utilisons des identifiants tels que TObject, TComponent, etc. pour représenter les classes, qui sont implémentées en tant que données VMT respectives en interne dans DELPHI. Le type de classe défini avec la classe de mot réservé est en fait un pointeur vers les données VMT pertinentes.
Pour notre application, les données VMT sont des données statiques. Une fois que le compilateur a compilé notre application, ces informations sur les données ont été déterminées et initialisées. Les instructions de programme que nous écrivons peuvent accéder aux informations liées à VMT, obtenir des informations telles que la taille de l'objet, le nom de la classe ou les données d'attribut d'exécution, ou appeler des méthodes virtuelles ou lire le nom et l'adresse de la méthode, etc.
Lorsqu'un objet est généré, le système alloue un espace mémoire pour l'objet et associe l'objet à la classe appropriée. Par conséquent, les 4 premiers octets de l'espace de données alloué pour l'objet deviennent des pointeurs vers les données de classe VMT.
Jetons un coup d'œil à la façon dont les objets naissent et meurent. En regardant mon fils de trois ans sauter sur l'herbe, c'est précisément parce que j'ai été témoin du processus de naissance de la vie que je peux vraiment comprendre le sens et la grandeur de la vie. Seuls ceux qui ont vécu la mort comprendront et chériront davantage la vie. Alors, comprenons le processus de création et de destruction des objets !
Nous savons tous que l’objet le plus simple peut être construit à l’aide de l’instruction suivante :
AnObject := TObject.Create;
Le compilateur implémente sa compilation comme :
En fonction du VMT correspondant à TObject, appelez le constructeur Create de TObject. Le constructeur Create appelle le processus ClassCreate du système, et le processus ClassCreate du système appelle la méthode virtuelle NewInstance via la classe VMT qui y est stockée. Le but de l'appel de la méthode NewInstance est d'établir l'espace d'instance de l'objet. Comme nous n'avons pas surchargé cette méthode, il s'agit de la NewInstance de la classe TObject. La méthode NewInstance de la classe TObjec appellera la procédure GetMem pour allouer de la mémoire à l'objet en fonction de la taille de l'instance d'objet (InstanceSize) initialisée par le compilateur dans la table VMT, puis appellera la méthode InitInstance pour initialiser l'espace alloué. La méthode InitInstance initialise d'abord les 4 premiers octets de l'espace objet en tant que pointeur vers le VMT correspondant à la classe d'objet, puis efface l'espace restant. Après avoir établi l’instance d’objet, une méthode virtuelle AfterConstruction est également appelée. Enfin, enregistrez le pointeur d'adresse des données d'instance d'objet dans la variable AnObject, et de cette façon, l'objet AnObject est né.
De même, un objet peut être détruit à l'aide de l'instruction suivante :
AnObject.Destroy ;
Le destructeur de TObject, Destroy, est déclaré comme une méthode virtuelle, qui est également l'une des méthodes virtuelles inhérentes au système. La méthode Destory appelle d’abord la méthode virtuelle BeforeDestruction, puis appelle le processus ClassDestroy du système. Le processus ClassDestory appelle la méthode virtuelle FreeInstance via la classe VMT, et la méthode FreeInstance appelle le processus FreeMem pour libérer l'espace mémoire de l'objet. C'est ainsi qu'un objet disparaît du système.
Le processus de destruction des objets est plus simple que le processus de construction des objets, tout comme la naissance de la vie est un long processus de gestation, mais la mort est relativement de courte durée. Cela semble être une règle inévitable.
Lors du processus de construction et de destruction de l'objet, deux fonctions virtuelles, NewInstance et FreeInstance, sont appelées pour créer et libérer l'espace mémoire de l'instance d'objet. La raison pour laquelle ces deux fonctions sont déclarées comme fonctions virtuelles est de permettre aux utilisateurs de disposer d'une marge d'expansion lors de l'écriture de classes d'objets spéciales qui nécessitent que les utilisateurs gèrent leur propre mémoire (comme dans certains programmes de contrôle industriel spéciaux).
Déclarer AfterConstruction et BeforeDestruction comme fonctions virtuelles, c'est également donner à la classe dérivée dans le futur la possibilité de laisser l'objet nouvellement né respirer la première bouffée d'air frais après avoir généré l'objet, et de permettre à l'objet de terminer les conséquences avant que l'objet ne meure. . Tout cela a du sens. En fait, l'événement OnCreate et l'événement OnDestroy de l'objet TForm et de l'objet TDataModule sont déclenchés respectivement dans les deux processus de fonction virtuelle de surcharge TForm et TDataModule.
De plus, TObjec fournit également une méthode Free, qui n'est pas une méthode virtuelle. Elle est spécialement prévue pour libérer l'objet en toute sécurité lorsqu'il n'est pas clair si l'objet est vide (nil). En fait, si vous ne parvenez pas à déterminer si l'objet est vide, il existe un problème de logique de programme peu claire. Cependant, personne n’est parfait et ne peut faire d’erreurs. C’est aussi une bonne chose d’utiliser Free pour éviter les erreurs accidentelles. Cependant, l’écriture de programmes corrects ne peut pas s’appuyer uniquement sur de telles solutions. Le premier objectif de la programmation devrait être de garantir l’exactitude logique du programme !
Les amis intéressés peuvent lire le code original de l'unité système, où une grande quantité de code est écrite en langage assembleur. Les amis prudents peuvent constater que le constructeur Create et le destructeur Destory de TObject n'ont écrit aucun code. En fait, via la fenêtre Debug CPU dans l'état de débogage, le code d'assemblage de Create et Destory peut être clairement reflété. Parce que les maîtres qui ont créé DELPHI ne voulaient pas fournir aux utilisateurs trop de choses compliquées. Ils voulaient que les utilisateurs écrivent des applications basées sur des concepts simples et cachent le travail complexe à entreprendre à l'intérieur du système. Par conséquent, lors de la publication de l'unité System.pas, les codes de ces deux fonctions sont spécialement supprimés pour faire croire aux utilisateurs que TObject est la source de toutes choses et que les classes dérivées de l'utilisateur partent complètement du néant. Bien que la lecture de ces codes les plus essentiels de DELPHI nécessite une petite quantité de connaissances en langage assembleur, la lecture de ces codes peut nous donner une compréhension plus approfondie de l'origine et du développement du monde DELPHI. Même si vous ne comprenez pas grand chose, être capable au moins de comprendre certaines choses de base nous sera d'une grande aide pour écrire des programmes DELPHI.
Section 2 TClass Atome
Dans l'unité System.pas, TClass est défini comme ceci :
TClass = classe de TObject ;
Cela signifie que TClass est la classe de TObject. Parce que TObject lui-même est une classe, TClass est ce qu'on appelle la classe des classes.
Conceptuellement, TClass est un type de classe, c'est-à-dire une classe. Cependant, nous savons qu'une classe de DELPHI représente une donnée VMT. La classe peut donc être considérée comme le type défini pour la donnée VMT. En fait, il s’agit d’un type pointeur pointant vers les données VMT !
Dans le langage C++ traditionnel précédent, le type d’une classe ne pouvait pas être défini. Une fois l'objet compilé, il est corrigé, les informations structurelles de la classe ont été converties en code machine absolu et les informations complètes de la classe n'existeront pas dans la mémoire. Certains langages orientés objet de niveau supérieur peuvent prendre en charge l'accès dynamique et l'invocation d'informations de classe, mais ils nécessitent souvent un mécanisme d'interprétation interne complexe et davantage de ressources système. Le langage Pascal Objet de DELPHI absorbe certaines des excellentes fonctionnalités des langages orientés objet de haut niveau, tout en conservant l'avantage traditionnel de compiler directement les programmes en code machine, ce qui résout parfaitement les problèmes de fonctions avancées et d'efficacité des programmes.
C'est précisément parce que DELPHI conserve des informations complètes sur les classes dans l'application qu'il peut fournir des fonctions avancées orientées objet telles que la conversion et l'identification des classes au moment de l'exécution, dans lesquelles les données VMT de la classe jouent un rôle central clé. Les amis intéressés peuvent lire les deux processus d'assemblage d'AsClass et IsClass dans l'unité Système. Ce sont les codes d'implémentation des opérateurs as et is pour approfondir leur compréhension des classes et des données VMT.
...
Le contenu suivant comprend également la compréhension des constructeurs fictifs, le mécanisme d'implémentation de l'interface et le mécanisme d'implémentation de la gestion des exceptions, etc. Les principes de base de DLPHI. J'espère pouvoir le terminer après le 1er mai et le partager avec tout le monde.