resueltos relaciones que programacion polimorfismo orientada objetos objeto herencia entre ejercicios constructores clases c++ inheritance rtti typeid

relaciones - que es un objeto en c++



¿Cómo usar RTTI para determinar el gráfico de herencia en C++? (5)

No es posible usar std::type_info ya que no proporciona una manera de consultar información de herencia o convertir un objeto std::type_info a su tipo correspondiente para que pueda hacer el reparto.

Si tiene una lista de todos los tipos posibles que necesita almacenar en sus objetos use boost::variant y su visitante.

¿Qué construcciones c ++, si existen, están ahí para enumerar los antepasados ​​de una clase en tiempo de ejecución?

Básicamente, tengo una clase que almacena un puntero a cualquier objeto, incluido posiblemente un tipo primitivo (algo así como boost::any , que no quiero usar porque necesito conservar la propiedad de mis objetos). Internamente, este puntero es un void* , pero el objetivo de esta clase es envolver el void* con la seguridad de tipo de tiempo de ejecución. El operador de asignación está modelado, así que en el momento de la asignación tomo el typeid() del puntero entrante y lo almacena. Luego, cuando retroceda más tarde, puedo verificar el typeid() del tipo de type_info el type_info almacenado. Si no coincide, el elenco emitirá una excepción.

Pero hay un problema: parece que pierdo el polimorfismo. Digamos que B es una base de D Si type_info un puntero a D en mi clase, el type_info almacenado también será de D Luego, más adelante, es posible que desee recuperar un puntero B Si utilizo el método de mi clase para convertirlo a B* , entonces typeid(B) == typeid(D) falla, y el molde genera una excepción, aunque la conversión D->B es segura. Dynamic_cast<>() no se aplica aquí, ya que estoy operando en un void* y no en un ancestro de B o D

Lo que me gustaría poder hacer es verificar is_ancestor(typeid(B), typeid(D)) . es posible? (¿Y no es esto lo que dynamic_cast<> está haciendo detrás de escena?)

Si no, entonces estoy pensando en tomar un segundo enfoque de todos modos: implementar una clase TypeInfo , cuyas clases derivadas son singletons con plantilla. Luego puedo almacenar la información que quiera en estas clases, y luego mantener los punteros en mi clase AnyPointer . Esto me permitiría generar / almacenar la información del antepasado en tiempo de compilación de una manera más accesible. Así que la opción n. ° 1 (una forma incorporada de enumerar ancestros con solo información disponible en tiempo de ejecución), hay una construcción / procedimiento que puedo usar que permitirá que la información del antepasado se genere y almacene automáticamente en tiempo de compilación, preferiblemente sin tener que ingresar explícitamente que "la clase A deriva de B y C ; C deriva de D ", etc. Una vez que tengo esto, ¿hay una manera segura de realizar ese lanzamiento?


Si bien no puedo pensar en ninguna forma de implementar la opción n. ° 1, la opción n. ° 2 debería ser factible si puede generar una lista en tiempo de compilación de las clases que le gustaría usar. Filtre esta lista de tipos con boost :: MPL y la is_base_of is_base_of para obtener una lista de tipos de caracteres válidos, que se pueden comparar con el tipo de letra guardado.


Primero, lo que está pidiendo no se puede implementar solo encima de type_info .

En C ++, para que un lanzamiento ocurra de un objeto a otro, se necesita algo más que asumir a ciegas que un tipo se puede usar como otro, también se necesita ajustar el puntero, debido a la herencia múltiple (compensación en tiempo de compilación) y la herencia virtual (desplazamiento en tiempo de ejecución).

La única forma de emitir de forma segura un valor de un tipo a otro es usar static_cast (funciona para herencia única o múltiple) y dynamic_cast (también funciona para la herencia virtual y realmente comprueba los valores de tiempo de ejecución).

Desafortunadamente, esto es realmente incompatible con el borrado de tipos (la antigua incompatibilidad template-virtual ).

Si se limita a la herencia no virtual, creo que debería ser posible lograr esto almacenando las compensaciones de las conversiones en varias bases en algunos datos de Configuration (los únicos de los que está hablando).

Para la herencia virtual, solo puedo pensar en un mapa de pares de type_info a un void* (*caster)(void*) .

Y todo esto requiere enumerar los posibles moldes manualmente :(


La información es (a menudo) allí dentro de la implementación. Sin embargo, no hay una forma estándar de C ++ para acceder, no está expuesto. Si está dispuesto a vincularse a implementaciones específicas o conjuntos de implementaciones, puede jugar un juego sucio para encontrar la información.

Un ejemplo para gcc, usando el Itanium ABI es:

#include <cassert> #include <typeinfo> #include <cxxabi.h> #include <iostream> bool is_ancestor(const std::type_info& a, const std::type_info& b); namespace { bool walk_tree(const __cxxabiv1::__si_class_type_info *si, const std::type_info& a) { return si->__base_type == &a ? true : is_ancestor(a, *si->__base_type); } bool walk_tree(const __cxxabiv1::__vmi_class_type_info *mi, const std::type_info& a) { for (unsigned int i = 0; i < mi->__base_count; ++i) { if (is_ancestor(a, *mi->__base_info[i].__base_type)) return true; } return false; } } bool is_ancestor(const std::type_info& a, const std::type_info& b) { if (a==b) return true; const __cxxabiv1::__si_class_type_info *si = dynamic_cast<const __cxxabiv1::__si_class_type_info*>(&b); if (si) return walk_tree(si, a); const __cxxabiv1::__vmi_class_type_info *mi = dynamic_cast<const __cxxabiv1::__vmi_class_type_info*>(&b); if (mi) return walk_tree(mi, a); return false; } struct foo {}; struct bar : foo {}; struct baz {}; struct crazy : virtual foo, virtual bar, virtual baz {}; int main() { std::cout << is_ancestor(typeid(foo), typeid(bar)) << "/n"; std::cout << is_ancestor(typeid(foo), typeid(baz)) << "/n"; std::cout << is_ancestor(typeid(foo), typeid(int)) << "/n"; std::cout << is_ancestor(typeid(foo), typeid(crazy)) << "/n"; }

Donde echo el type_info al tipo real que se usa internamente y luego lo uso recursivamente para recorrer el árbol de herencia.

No recomendaría hacer esto en código real, pero como un ejercicio de detalles de implementación no es imposible.


¡Tuve un problema similar que resolví a través de excepciones! Escribí un artículo sobre eso:

http://drdobbs.com/cpp/229401004

De acuerdo. Siguiendo el consejo de Peter, el bosquejo de la idea sigue. Se basa en el hecho de que si D deriva de B y se lanza un puntero a D , entonces se activará una cláusula catch que espera un puntero a B

Entonces uno puede escribir una clase (en mi artículo la he llamado any_ptr ) cuyo constructor de plantilla acepta una T* y almacena una copia de ella como un void* . La clase implementa un mecanismo que envía estáticamente el void* a su tipo original T* y arroja el resultado. Se activará una cláusula catch que espera U* donde U = T o U es una base de T y esta estrategia es la clave para implementar una prueba como en la pregunta original.

EDITAR: (por Matthieu M. para las respuestas son las mejores autónomas, por favor refiérase al Dr. Dobbs para la respuesta completa)

class any_ptr { void* ptr_; void (*thr_)(void*); template <typename T> static void thrower(void* ptr) { throw static_cast<T*>(ptr); } public: template <typename T> any_ptr(T* ptr) : ptr_(ptr), thr_(&thrower<T>) {} template <typename U> U* cast() const { try { thr_(ptr_); } catch (U* ptr) { return ptr; } catch (...) {} return 0; } };