Puede ser útil ver el código ensamblador que se genera con la llamada a una función virtual, para comprobar que efectivamente se produce la ligadura dinámica. Aquí está la salida de un compilador para la llamada
i.adjust(1);
dentro de la función f(Instrument& i):
push 1 push si mov bx, word ptr [si] call word ptr [bx+4] add sp, 4
Los argumentos de una llamada a una función C++, como los de una llamada a una función C, son colocados en la pila de derecha a izquierda (este orden es necesario para poder soportar las listas de argumentos variables de C), por lo que el argumento 1 se pone primero en la pila. En este punto de la función, el registro si (que es parte de la arquitectura del procesador Intel™ X86™) contiene la dirección de i. También se introduce en la pila porque es la dirección de comienzo del objeto de interés. Hay que recordar que la dirección del comienzo del objeto corresponde al valor de this, y this es introducido en la pila de manera oculta como argumento antes de cada llamada a una función miembro, por lo que la función miembro sabe sobre qué objeto en concreto está trabajando. Debido a esto, antes de una llamada a una función miembro siempre se verá que se introduce en la pila un argumento más de los que tiene la llamada (excepto para las funciones miembro static, que no tienen this).
Ahora hay que realizar la llamada propiamente dicha a la función virtual. Primero hay que obtener el VPTR para poder encontrar la VTABLE. En este compilador el VPTR se inserta al principio del objeto, por lo que el contenido de this corresponde al VPTR. La línea
mov bx, word ptr [si]
obtiene la palabra (word) a la que apunta si (es decir, this), que es el VPTR, y lo coloca dentro del registro bx.
El VPTR contenido en bx apunta a la dirección inicial de la VTABLE, pero el puntero de la función a llamar no se encuentra en la posición cero de la VTABLE, sino en la posición dos (debido a que es la tercera función en la lista). Con este modelo de memoria cada puntero a función ocupa dos bytes, por lo que el compilador suma cuatro al VPTR para calcular dónde está la dirección de la función apropiada. Hay que hacer notar que éste es un valor constante establecido en tiempo de compilación, por lo que lo único que importa es que el puntero a función que está en la posición dos sea el de adjust(). Afortunadamente, el compilador se encarga de todo y se asegura de que todos los punteros a funciones en todas las VTABLEs de una jerarquía particular se creen con el mismo orden, independientemente del orden en que se hayan redefinido las funciones en las clases derivadas.
Una vez se ha calculado en la VTABLE la dirección del puntero apropiado, se llama a la función a la que apunta el puntero. Esto es, se busca la dirección y se llama de una sola vez con la sentencia:
call word ptr [bx+4]
Finalmente, se retrocede el puntero de la pila para limpiar los argumentos que se pusieron antes de la llamada. En código ensamblador de C y de C++ se ve a menudo que es la función que hace la llamada quien limpia los argumentos, pero esto puede variar dependiendo del procesador o de la implementación del compilador.