c++ gcc virtual-functions vtable

c++ - ¿Cuál es la primera entrada(int(*)(...)) 0 vtable en la salida de g++-fdump-class-hierarchy?



gcc virtual-functions (2)

Esos son los punteros offset-to-top (necesario para herencias múltiples) y typeinfo (RTTI).

Desde el Itanium ABI (no está utilizando el compilador Itanium, pero su descripción de esto es realmente buena) :

El desplazamiento hacia arriba mantiene el desplazamiento hacia la parte superior del objeto desde la ubicación dentro del objeto del puntero de tabla virtual que se dirige a esta tabla virtual, como ptrdiff_t. Siempre está presente El desplazamiento proporciona una forma de encontrar la parte superior del objeto desde cualquier subobjeto base con un puntero de tabla virtual. Esto es necesario para dynamic_cast en particular.
(En una tabla virtual de objetos completa, y por lo tanto en todas sus tablas virtuales base primarias, el valor de esta compensación será cero. [...])

El puntero typeinfo apunta al objeto typeinfo utilizado para RTTI. Siempre está presente Todas las entradas en cada una de las tablas virtuales para una clase determinada deben apuntar al mismo objeto typeinfo. Una implementación correcta de la igualdad typeinfo es verificar la igualdad del puntero, excepto los punteros (directa o indirectamente) a tipos incompletos. El puntero typeinfo es un puntero válido para las clases polimórficas, es decir, aquellas con funciones virtuales, y es cero para las clases no polimórficas.

Offset-to-top en más detalle (por solicitud)

Supongamos que tiene una clase D derivada que deriva de una clase base, B1 . ¿Qué sucede cuando intentas lanzar una instancia D para escribir B1 ? Dado que las funciones que toman un objeto B1 no saben nada acerca de D , parte del Dtabletable también debe ser una tabla válida B1 . Esto es bastante fácil: simplemente haga que el inicio de la tabla Dtable parezca una tabla de presentación B1 y agregue las entradas adicionales que necesitamos después de eso. Las funciones que esperan un B1 estarán contentas, ya que no usarán ninguna parte del vtable más allá de lo que esperan para un B1 .

Sin embargo, ¿qué sucede si D ahora también deriva de B2 ? ¡El puntero al vtable de D no puede ser tanto un vtable B1 válido como un vtable B2 válido! El compilador resuelve esto agregando un vtable B2 separado al final de nuestro Vtable D/B1 combinado, y ajusta el puntero vtable manualmente cuando tratamos de transmitir desde un D a un B2 .

Sin embargo, esto lleva a un nuevo problema: ¿qué sucede cuando intentamos regresar de un B2 a un D ? El compilador no puede simplemente ajustar el puntero-vtable hacia atrás en la misma cantidad en que ajustó el puntero previamente, ¡porque en realidad no sabe con certeza que el objeto B2 que le estamos dando es del tipo D ! En particular, dynamic_cast<D>() debe poder decir si nuestro objeto es o no de tipo D Para eso, necesita acceder al RTTI del objeto, y para eso , necesita saber dónde está el inicio del vtable del objeto original. Este es el propósito del valor offset-to-top: nos da el desplazamiento al inicio del vtable del objeto original, obtenemos RTTI de nuestro objeto y el dios vengativo de C ++ permite que nuestros cultivos crezcan durante otra temporada.

Esta página tiene algunos buenos ejemplos de diseños vtable (en la Tabla 1c ). Tenga en cuenta que son un poco más complicadas debido al uso de la herencia virtual , que agrega un desplazamiento adicional al vtable de cada clase secundaria.

Para este código:

class B1{ public: virtual void f1() {} }; class D : public B1 { public: void f1() {} }; int main () { B1 *b1 = new B1(); D *d = new D(); return 0; }

Después de la compilación, el vtable que obtengo con g++ -fdump-class-hierarchy es:

Vtable for B1 B1::_ZTV2B1: 3u entries 0 (int (*)(...))0 8 (int (*)(...))(& _ZTI2B1) 16 B1::f1 Vtable for D D::_ZTV1D: 3u entries 0 (int (*)(...))0 8 (int (*)(...))(& _ZTI1D) 16 D::f1

No pude entender cómo se corresponden las entradas (int ( ) (...)) 0 *. Por supuesto, significa algo así como, es una función que devuelve un int y toma un número ilimitado de argumentos, no entiendo nada más. ¿A qué función se corresponde este puntero a la función? y como lo sabes? El mío es una máquina de 64 bits.

El segundo puntero a la función tiene una dirección asociada al final? ¿A quién le corresponde eso?

EDITAR

El compilador, yo uso es g ++:

g++ -v Using built-in specs. Target: x86_64-suse-linux Configured with: ../configure --prefix=/usr --infodir=/usr/share/info --mandir=/usr/share/man --libdir=/usr/lib64 --libexecdir=/usr/lib64 --enable-languages=c,c++,objc,fortran,obj-c++,java,ada --enable-checking=release --with-gxx-include-dir=/usr/include/c++/4.4 --enable-ssp --disable-libssp --with-bugurl=http://bugs.opensuse.org/ --with-pkgversion=''SUSE Linux'' --disable-libgcj --disable-libmudflap --with-slibdir=/lib64 --with-system-zlib --enable-__cxa_atexit --enable-libstdcxx-allocator=new --disable-libstdcxx-pch --enable-version-specific-runtime-libs --program-suffix=-4.4 --enable-linux-futex --without-system-libunwind --with-arch-32=i586 --with-tune=generic --build=x86_64-suse-linux Thread model: posix *gcc version 4.4.1 [gcc-4_4-branch revision 150839] (SUSE Linux)*


Tal vez la primera entrada es para un destructor virtual y la segunda es para el soporte de RTTI. Pero eso es solo una suposición.