¿Qué es la VMT?

6104 vistas

Para poder llamar a un determinado método en un determinado momento, un objeto tiene que apoyarse en una lista de sus métodos virtuales, la VMT o Virtual Methods Table. Esta tabla es iniciallizada por el constructor del objeto en el caso en que se declare un método virtual.

DEspués de la llamada del constructor, la instancia recibida no es más que un puntero hacia una zona de memoria que contiene la dirección de la VMT de la clase, después de las direcciones de los campos de la clase base, almacenados uno detrás de otro en el orden de la declaración como una sucesión de variables.
Los campos siempre están alineados y corresponden a un tipo unpacked record. Todos los campos heredades de una clase padre se almacenan antes de que los nuevos campos definidos en la clase descendiente, por lo que la última dirección es el último campo declarado en la nueva clase.

En los offsets positivos, una VMT se compone de una lista de punteros de métodos, uno para cada método virtual definido por el usuario, en el mismo orden de declaración de la calse.
En los offsets negativos, una VMT contiene un cierto número de campos usados internamente.

Podemos decir, por lo tanto, que un objeto es un puntero a la VMT.

Toda clase posee su propia VMT, conservando siempre un enlace con la VMT de su padre (gerarquÃa de clases). Por lo tanto, cada vez se hace una copia de la VMT que se hereda de una clase.

La clase TObject declara varios métodos y una propiedad oculta para almacenar una referencia a la calse del objeto. Esta propiedad contiene un puntero hacia la VMT de a clase. Cada clase tiene una VMT única y todos los objetos de esa clase comparten la VMT de la clase.
Es decir, la herencia de una clase a partir de otra implica la herencia de la VMT completa de ésta última. Los métodos nuevos se añaden al final de la tabla.
La VMT de un objeto contiene  todos los métodos virtuales de sus padres como los que su clase decalra. Por esto, los métodos virtuales usan más memoria que los dinámicos (dynamic), aunque su ejecución es más rápida.

Cuando se hace una llamada a un método virtual, el objeto busca en su VMT si lo encuentra. Si es el caso, usará la dirección almacenada y ejecutará el método. Sino, recorrerá l aVMT de su padre directo y asà hasta el ancestro más alejado de la gerarquÃa.

Del mismo modo, cuando un método sobrecargado hace una llamada a un método del padre, se efectuará una búsqueda a partir de la VMT del primer padre.
Esto es debido a que un método virtual se liga al momento de ejecución y no en la compilación.

La VMT será destruida por el destructor de la clase cuando ya no se necesite más.

Una referencia de clase se implementa como un puntero hacia la VMT de la clase.

Traducción de una tabla extraÃda de un artÃculo de Brian Long:

Offset...Nombre de constante....Descripción
--------------------------------------------------------------------------------------------------------------
-76......vmtSelfPtr..................Dirección de la 1era entrada de la VMT si existe, sino del nombre de la clase *

-72......vmtIntfTable...............Dirección de la tabla de las interfaces implementadas *

-68......vmtAutoTable..............Dirección de la sección de las 'class automated' (Delphi 2) *

-64......vmtInitTable...............Dirección de la tabla de campos que requieren inicialización *

-60......vmtTypeInfo...............Dirección de información RTTI *

-56......vmtFieldTable..............Dirección de la tabla de los campos publicados (published) *

-52......vmtMethodTable...........Dirección de la tabla de los metodos publicados (published) *

-48......vmtDynamicTable.........Dirección de la (tabla de métodos dinámicos - dynamic) *

-44......vmtClassName.............Dirección del nombre de la clase, cadena de caráteres/string

-40......vmtInstanceSize...........Tamaño en bytes de una instancia del objeto

-36......vmtParent....................Dirección de la VMT de la clase padre *

-32......vmtSafeCallException....Dirección del método virtual SafeCallException *

-28......vmtAfterConstruction.....Dirección del método virtual AfterConstruction

-24......vmtBeforeDestruction.....Dirección del método virtual BeforeDestruction

-20......vmtDispatch...................Dirección del método virtual Dispatch

-16......vmtDefaultHandler.........Dirección del método virtual DefaultHandler

-12......vmtNewInstance............Dirección del método virtual NewInstance

-8......vmtFreeInstance.............Dirección del método virtual FreeInstance

-4......vmtDestroy....................Dirección del método virtual Destroy

Las entradas indicadas con * serán nil si no se han informado. Esta disposición es compatible con una v-table de C++ y com COM.

La VMT contienen la dirección de los métodos virtuales (declarados con la directiva virtual o override) pero no incluye las direcciones definidas dentro de TObject (SafeCallException, AfterConstruction, BeforeDestruction, Dispatch, DefaultHandler, NewInstance, FreeInstance y el destructor Destroy) que serán informados antes de la VMT.

En lugar de usar la directiva virtual podemos usar la directiva dynamic. la semántica es la misma, pero la implementación es diferente. La búsqueda de un método virtual dentro de la VMT es rápido ya que el compilador genera un Ãndice directamente dentro de la VMT.

Para terminar decir que la directiva override reemplaza, dentro de la VMT, la dirección original del método en cuestión por la dirección del nuevo método.