ejemplo - i o c++
¿Cómo se implementa dynamic_cast? (3)
¿Cómo puede el
dynamic_cast
saber siDerived2
se derivó deDerived
(quéDerived2
siDerived
se declaró en una biblioteca diferente)?
La respuesta es sorprendentemente simple: dynamic_cast
puede saberlo manteniendo este conocimiento.
Cuando el compilador genera código, mantiene los datos sobre las jerarquías de clases en algún tipo de tabla que dynamic_cast
pueda consultar más adelante. Esa tabla se puede adjuntar al puntero vtable para una fácil búsqueda por la implementación dynamic_cast
. Los datos necesarios para el tipo de typeid
para esas clases también se pueden almacenar junto con aquellos.
Si las bibliotecas están involucradas, este tipo de cosas generalmente requiere que estas estructuras de información de tipo estén expuestas en las bibliotecas, al igual que con las funciones. Es posible, por ejemplo, obtener un error de vinculador que se parece a "Referencia indefinida a ''vtable for XXX''" (y ¡chico, son esos molestos!), De nuevo, igual que con las funciones.
Considera esta simple jerarquía:
class Base { public: virtual ~Base() { } };
class Derived : public Base { };
Intentar bajar la Base* p
a Derived*
es posible usando dynamic_cast<Derived*>(p)
. Solía pensar que dynamic_cast
funciona comparando el puntero vtable en p
con el de un objeto Derived
.
Pero ¿y si derivamos otra clase de Derived
? Ahora tenemos:
class Derived2 : public Derived { };
En este caso:
Base* base = new Derived2;
Derived* derived = dynamic_cast<Derived*>(base);
Todavía tenemos un downcast exitoso, a pesar de que el puntero vtable en Derived2
no tiene nada que ver con un puntero vtable en Derived
.
¿Cómo funciona realmente? ¿Cómo puede el dynamic_cast
saber si Derived2
se derivó de Derived
(qué Derived2
si Derived
se declaró en una biblioteca diferente)?
Estoy buscando detalles específicos sobre cómo funciona esto (preferiblemente en GCC, pero otros también están bien). Esta pregunta no es un duplicado de esta pregunta (que no especifica cómo funciona realmente).
¿Cómo puede saber Dynamic_cast si Derived2 se derivó de Derived (¿qué pasa si Derived fue declarado en una biblioteca diferente)?
El dynamic_cast
sí mismo no sabe nada, es el compilador que conoce esos hechos. Un vtable no necesariamente contiene solo punteros a funciones virtuales.
Así es como lo haría (ingenuamente): mi vtable contendrá puntero (s) a algún tipo de información (RTTI) utilizada por dynamic_cast
. El RTTI para un tipo contendrá punteros a clases base, por lo que puedo subir la jerarquía de clases. El pseudocódigo para el reparto se vería así:
Base* base = new Derived2; //base->vptr[RTTI_index] points to RTTI_of(Derived2)
//dynamic_cast<Derived*>(base):
RTTI* pRTTI = base->vptr[RTTI_index];
while (pRTTI && *pRTTI != RTTI_of(Derived))
{
pRTTI = pRTTI->ParentRTTI;
}
if (pRTTI) return (Derived*)(base);
return NULL;
Magia.
Es una broma. Si realmente desea investigar esto en detalle, el código que lo implementa para GCC está en libsupc ++, una parte de libstdc ++.
https://github.com/mirrors/gcc/tree/master/libstdc%2B%2B-v3/libsupc%2B%2B
Específicamente, busque todos los archivos con tinfo o type_info en su nombre.
O lea la descripción aquí, probablemente sea mucho más accesible:
https://itanium-cxx-abi.github.io/cxx-abi/abi.html#rtti
Esto detalla el formato de la información de tipo que genera el compilador y debe darle pistas sobre cómo el soporte de tiempo de ejecución encuentra la ruta de conversión correcta.