En el proceso de utilizar DELPHI para desarrollar software, somos como un grupo de vacas y ovejas felices en la pradera, disfrutando despreocupadamente de la luz del sol que nos brinda el lenguaje Object Pascal y las ricas plantas acuáticas proporcionadas por varios controles VCL. Mirando hacia el infinito cielo azul, mirando hacia la exuberante hierba verde de la tierra, ¿quién pensaría en lo grande que es el universo y qué cosas son más pequeñas que las moléculas y los átomos? Ésa es una cuestión de filósofos. En ese momento, el filósofo estaba sentado en la cima de una montaña alta, mirando los cambios en las nebulosas del universo, mirando los insectos que se arrastraban por el suelo, de repente se volvió, asintió y sonrió a nuestro grupo de pastoreo. ganado vacuno y ovino. Cogió un trozo de hierba, lo sostuvo suavemente en su boca, cerró los ojos y lo probó con atención. Me pregunto cuál sería el sabor de este trozo de hierba en la boca del filósofo. Sin embargo, siempre tenía una sonrisa de satisfacción en su rostro.
Conocer y comprender el mundo atómico microscópico de DELPHI puede permitirnos comprender a fondo la estructura de aplicación macroscópica de DELPHI, desarrollando así nuestro software en un espacio ideológico más amplio. Es como si Newton descubriera el movimiento de los objetos macroscópicos, pero estuviera preocupado porque no podía entender por qué los objetos se movían así. Por el contrario, Einstein experimentó la vida feliz de la relatividad entre las leyes de las partículas básicas y el movimiento de los objetos macroscópicos. !
Sección 1 Átomo objeto
¿Qué es TObject?
Es el núcleo básico de la arquitectura del lenguaje Object Pascal y el origen de varios controles VCL. Podemos pensar en TObject como uno de los átomos que componen una aplicación DELPHI. Por supuesto, están formados por partículas más sutiles, como elementos básicos de sintaxis de Pascal.
Se dice que TObject es el átomo del programa DELPHI porque TObject es compatible internamente con el compilador DELPHI. Todas las clases de objetos se derivan de TObject, incluso si no especifica TObject como clase antecesora. TObject se define en la unidad del sistema, que forma parte del sistema. Al comienzo de la unidad System.pas, se encuentra este texto de comentario:
{PConstantes, tipos, procedimientos redefinidos, }
{ y funciones (como True, Integer o }
{Writeln) no tienen declaraciones reales.}
{En lugar de eso, están integrados en el compilador}
{ y son tratados como si fueran declarados }
{al principio de la unidad del sistema.}
Significa que esta unidad contiene constantes, tipos, procedimientos y funciones predefinidos (como: True, Integer o Writeln). En realidad, no están declarados, pero el compilador los incorpora y se considera que se utilizan al comienzo de la compilación. ser una definición establecida. Puede agregar otros archivos de programa fuente como Classes.pas o Windows.pas a su archivo de proyecto para compilar y depurar el código fuente, ¡pero no puede agregar el archivo de programa fuente System.pas a su archivo de proyecto para su compilación! ¡DELPHI informará errores de compilación para definiciones duplicadas de System!
Por lo tanto, TObject es una definición proporcionada internamente por el compilador. Para aquellos de nosotros que usamos DELPHI para desarrollar programas, TObject es algo atómico.
La definición de TObject en la unidad del Sistema es la siguiente:
TObjeto = clase
constructor Crear;
procedimiento Gratis;
función de clase InitInstance (Instancia: Puntero): TObject;
procedimiento CleanupInstance;
función Tipo de clase: TClass;
función de clase ClassName: ShortString;
función de clase ClassNameIs (nombre constante: cadena): booleano;
función de clase ClassParent: TClass;
función de clase ClassInfo: puntero;
función de clase Tamaño de instancia: Entero largo;
función de clase Hereda de (AClass: TClass): booleano;
función de clase Dirección de método (nombre constante: cadena corta): puntero;
función de clase Nombre del método (Dirección: Puntero): Cadena corta;
función Dirección de campo (nombre constante: cadena corta): puntero;
función GetInterface(const IID: TGUID; out Obj): booleano;
función de clase GetInterfaceEntry(const IID: TGUID): PInterfaceEntry;
función de clase GetInterfaceTable: PInterfaceTable;
función SafeCallException(ExceptObject: TObject;
ExceptAddr: Puntero): HResult virtual;
procedimiento Después de la Construcción virtual;
procedimiento Antes de la Destrucción; virtual;
procedimiento Despacho(var Mensaje);
procedimiento DefaultHandler(var Mensaje);
función de clase NewInstance: TObject;
procedimiento FreeInstance;
destructor Destruir; virtual;
fin;
A continuación, llamaremos gradualmente a la puerta de los átomos del objeto para ver qué estructura hay en su interior.
Sabemos que TObject es la clase básica de todos los objetos, entonces, ¿qué es exactamente un objeto?
¡Cualquier objeto en DELPHI es un puntero, que indica el espacio ocupado por el objeto en la memoria! Aunque el objeto es un puntero, cuando nos referimos a los miembros del objeto, no necesitamos escribir el código MyObject^.GetName, solo podemos escribir MyObject.GetName. Esta es una sintaxis expandida del lenguaje Object Pascal. soportado por el compilador. Los amigos que usan C++ Builder tienen muy clara la relación entre objetos y punteros, porque los objetos en C++ Builder deben definirse como punteros. El lugar señalado por el puntero del objeto es el espacio del objeto donde el objeto almacena datos. Analicemos la estructura de datos del espacio de memoria señalado por el puntero del objeto.
Los primeros 4 bytes del espacio de objetos apuntan a la tabla de direcciones del método virtual (Tabla de métodos virtuales VMT? C) de la clase de objeto. El siguiente espacio es el espacio para almacenar los datos de los miembros del objeto en sí, y se almacena en el orden total desde los miembros de datos de la clase ancestral más primitiva del objeto hasta los miembros de datos de la clase de objeto, y en el orden en que Los miembros de datos se definen en cada nivel de clase.
La tabla de métodos virtuales (VMT) de una clase contiene las direcciones de procedimiento de los métodos virtuales de todas las clases derivadas de la clase antecesora original de la clase. El método virtual de una clase es un método declarado con la palabra reservada virtual. El método virtual es el mecanismo básico para lograr el polimorfismo de objetos. Aunque los métodos dinámicos declarados con la palabra reservada dinámica también pueden lograr polimorfismo de objetos, dichos métodos no se almacenan en la tabla de direcciones de métodos virtuales (VMT). Es solo otro método proporcionado por Object Pascal que puede ahorrar espacio de almacenamiento de clases. pero a expensas de la velocidad de llamada.
Incluso si no definimos ningún método virtual de la clase nosotros mismos, el objeto de la clase todavía tiene un puntero a la tabla de direcciones del método virtual, pero la longitud de la entrada de dirección es cero. Sin embargo, ¿dónde se almacenan los métodos virtuales definidos en TObject, como Destroy, FreeInstance, etc.? Resulta que las direcciones de sus métodos se almacenan en un desplazamiento espacial en la dirección negativa con respecto al puntero VMT. De hecho, el espacio de datos desplazado en 76 bytes en la dirección negativa de la tabla VMT es la estructura de datos del sistema de la clase de objeto. Estas estructuras de datos están relacionadas con el compilador y pueden cambiarse en futuras versiones de DELPHI.
Por lo tanto, se puede pensar que VMT es una estructura de datos que comienza desde el espacio de direcciones de desplazamiento negativo. El área de datos de desplazamiento negativo es el área de datos del sistema de VMT, y los datos de desplazamiento positivo de VMT son el área de datos del usuario (método virtual personalizado). tabla de direcciones). Las funciones y procedimientos relacionados con la información de clase o la información de tiempo de ejecución del objeto definidos en TObject generalmente están relacionados con los datos del sistema de VMT.
Un dato de VMT representa una clase. De hecho, ¡VMT es una clase! En Object Pascal, utilizamos identificadores como TObject, TComponent, etc. para representar clases, que se implementan como sus respectivos datos VMT internamente en DELPHI. El tipo de clase definida con la clase de palabra reservada es en realidad un puntero a los datos VMT relevantes.
Para nuestra aplicación, los datos VMT son datos estáticos. Después de que el compilador compila nuestra aplicación, esta información de datos se determina e inicializa. Las declaraciones de programa que escribimos pueden acceder a información relacionada con VMT, obtener información como el tamaño del objeto, el nombre de la clase o los datos de atributos de tiempo de ejecución, o llamar a métodos virtuales o leer el nombre y la dirección del método, etc.
Cuando se genera un objeto, el sistema asignará un espacio de memoria para el objeto y asociará el objeto con la clase relevante. Por lo tanto, los primeros 4 bytes en el espacio de datos asignado para el objeto se convierten en punteros a datos de clase VMT.
Echemos un vistazo a cómo nacen y mueren los objetos. Al ver a mi hijo de tres años saltando sobre el césped, es precisamente porque he sido testigo del proceso de nacimiento de la vida que puedo comprender verdaderamente el significado y la grandeza de la vida. Sólo aquellos que han experimentado la muerte comprenderán y apreciarán más la vida. Entonces, ¡comprendamos el proceso de creación y destrucción de objetos!
Todos sabemos que el objeto más simple se puede construir utilizando la siguiente declaración:
UnObjeto := TObject.Create;
El compilador implementa su compilación como:
Según el VMT correspondiente a TObject, llame al constructor Crear de TObject. El constructor Create llama al proceso ClassCreate del sistema, y el proceso ClassCreate del sistema llama al método virtual NewInstance a través de la clase VMT almacenada en él. El propósito de llamar al método NewInstance es establecer el espacio de instancia del objeto. Debido a que no hemos sobrecargado este método, es el NewInstance de la clase TObject. El método NewInstance de la clase TObjec llamará al procedimiento GetMem para asignar memoria para el objeto según el tamaño de la instancia del objeto (InstanceSize) inicializado por el compilador en la tabla VMT y luego llamará al método InitInstance para inicializar el espacio asignado. El método InitInstance primero inicializa los primeros 4 bytes del espacio de objetos como un puntero al VMT correspondiente a la clase de objeto y luego borra el espacio restante. Después de establecer la instancia del objeto, también se llama a un método virtual AfterConstruction. Finalmente, guarde el puntero de dirección de los datos de la instancia del objeto en la variable AnObject y de esta manera nace el objeto AnObject.
De manera similar, un objeto se puede destruir usando la siguiente declaración:
UnObjeto.Destruir;
El destructor de TObject, Destroy, se declara como un método virtual, que también es uno de los métodos virtuales inherentes al sistema. El método Destory primero llama al método virtual BeforeDestruction y luego llama al proceso ClassDestroy del sistema. El proceso ClassDestory llama al método virtual FreeInstance a través de la clase VMT, y el método FreeInstance llama al proceso FreeMem para liberar el espacio de memoria del objeto. Así, un objeto desaparece del sistema.
El proceso de destrucción de objetos es más simple que el proceso de construcción de objetos, al igual que el nacimiento de la vida es un proceso de gestación largo, pero la muerte es relativamente corta. Esto parece ser una regla inevitable.
Durante el proceso de construcción y destrucción del objeto, se llaman dos funciones virtuales, NewInstance y FreeInstance, para crear y liberar el espacio de memoria de la instancia del objeto. La razón por la que estas dos funciones se declaran como funciones virtuales es para permitir que los usuarios tengan espacio para la expansión al escribir clases de objetos especiales que requieren que los usuarios administren su propia memoria (como en algunos programas especiales de control industrial).
Declarar AfterConstruction y BeforeDestruction como funciones virtuales también le da a la clase derivada en el futuro la oportunidad de permitir que el objeto recién nacido respire el primer soplo de aire fresco después de generar el objeto y permitir que el objeto complete las consecuencias antes de que muera. Todo esto es algo que tiene sentido. De hecho, el evento OnCreate y el evento OnDestroy del objeto TForm y el objeto TDataModule se activan respectivamente en los dos procesos de funciones virtuales de sobrecarga de TForm y TDataModule.
Además, TObjec también proporciona un método gratuito, que no es un método virtual. Está especialmente diseñado para liberar objetos de forma segura cuando no está claro si está vacío (nulo). De hecho, si no puede determinar si el objeto está vacío, existe un problema de lógica del programa poco clara. Sin embargo, nadie es perfecto y puede cometer errores. También es bueno utilizar Gratis para evitar errores accidentales. Sin embargo, escribir programas correctos no puede depender únicamente de tales soluciones. ¡El primer objetivo de la programación debe ser garantizar la corrección lógica del programa!
Los amigos interesados pueden leer el código original de la unidad del sistema, donde una gran cantidad de código está escrito en lenguaje ensamblador. Los amigos cuidadosos pueden encontrar que el constructor Create y el destructor Destory de TObject no han escrito ningún código. De hecho, a través de la ventana Debug CPU en el estado de depuración, el código ensamblador de Create y Destory se puede reflejar claramente. Porque los maestros que crearon DELPHI no querían proporcionar a los usuarios demasiadas cosas complicadas. Querían que los usuarios escribieran aplicaciones basadas en conceptos simples y ocultaran el trabajo complejo dentro del sistema para que ellos lo realizaran. Por lo tanto, al publicar la unidad System.pas, los códigos de estas dos funciones se eliminan especialmente para que los usuarios piensen que TObject es la fuente de todas las cosas, y las clases derivadas del usuario comienzan completamente desde la nada. Aunque leer estos códigos más esenciales de DELPHI requiere una pequeña cantidad de conocimiento del lenguaje ensamblador, leer dichos códigos puede brindarnos una comprensión más profunda del origen y desarrollo del mundo DELPHI. Incluso si no comprende mucho, ser capaz de comprender al menos algunas cosas básicas nos será de gran ayuda a la hora de escribir programas DELPHI.
Sección 2 Átomo de clase T
En la unidad System.pas, TClass se define así:
TClass = clase de TObject;
Significa que TClass es la clase de TObject. Debido a que TObject en sí es una clase, TClass es la llamada clase de clases.
Conceptualmente, TClass es un tipo de clase, es decir, una clase. Sin embargo, sabemos que una clase de DELPHI representa un dato de VMT. Por lo tanto, la clase puede considerarse como el tipo definido para el elemento de datos VMT. De hecho, es un tipo de puntero que apunta a los datos VMT.
En el lenguaje C++ tradicional anterior, no se podía definir el tipo de clase. Una vez que se compila el objeto, se repara, la información estructural de la clase se convierte en código de máquina absoluto y la información completa de la clase no existirá en la memoria. Algunos lenguajes orientados a objetos de nivel superior pueden admitir el acceso dinámico y la invocación de información de clase, pero a menudo requieren un mecanismo de interpretación interno complejo y más recursos del sistema. El lenguaje Object Pascal de DELPHI absorbe algunas de las excelentes características de los lenguajes orientados a objetos de alto nivel, al tiempo que conserva la ventaja tradicional de compilar programas directamente en código de máquina, lo que resuelve perfectamente los problemas de funciones avanzadas y eficiencia del programa.
Es precisamente porque DELPHI retiene información completa de la clase en la aplicación que puede proporcionar funciones avanzadas orientadas a objetos, como convertir e identificar clases en tiempo de ejecución, en las que los datos VMT de la clase desempeñan un papel central clave. Los amigos interesados pueden leer los dos procesos de ensamblaje de AsClass e IsClass en la unidad del sistema. Son los códigos de implementación de los operadores as e is para profundizar su comprensión de las clases y los datos VMT.
...
El siguiente contenido también incluye la comprensión de constructores ficticios, el mecanismo de implementación de la interfaz y el mecanismo de implementación del manejo de excepciones, etc. Los principios básicos de DLPHI. Espero poder terminarlo después del Primero de Mayo y contribuirlo a todos.