En programación informática, la información de tipo en tiempo de ejecución o la identificación de tipo en tiempo de ejecución ( RTTI ) [1] es una característica de los lenguajes de programación C ++ y Object Pascal que expone información sobre el tipo de datos de un objeto en tiempo de ejecución . La información de tipo en tiempo de ejecución se puede aplicar a tipos de datos simples, como enteros y caracteres, o a tipos genéricos. Esta es una especialización de un concepto más general llamado introspección de tipos .
En el diseño original de C ++, Bjarne Stroustrup no incluía información de tipo en tiempo de ejecución, porque pensaba que este mecanismo a menudo se usaba incorrectamente. [2]
Descripción general
En C ++, RTTI se puede utilizar para realizar encasillamientos seguros , utilizando el dynamic_cast<>
operador, y para manipular la información de tipos en tiempo de ejecución, utilizando el typeid
operador y la std::type_info
clase. En Object Pascal, RTTI se puede utilizar para realizar conversiones de tipo seguro con el as
operador, probar la clase a la que pertenece un objeto con el is
operador y manipular la información de tipo en tiempo de ejecución con clases contenidas en la RTTI
unidad [3] (es decir, clases: TRttiContext , TRttiInstanceType , etc.).
RTTI está disponible solo para clases que son polimórficas , lo que significa que tienen al menos un método virtual . En la práctica, esto no es una limitación porque las clases base deben tener un destructor virtual para permitir que los objetos de las clases derivadas realicen una limpieza adecuada si se eliminan de un puntero base.
RTTI es opcional con algunos compiladores; el programador puede elegir en tiempo de compilación si desea incluir la funcionalidad. Puede haber un costo de recursos para hacer que RTTI esté disponible incluso si un programa no lo usa.
C ++ - typeid
La typeid
palabra clave se utiliza para determinar la clase de un objeto en tiempo de ejecución . Devuelve una referencia al std::type_info
objeto, que existe hasta el final del programa. [4] El uso de typeid
, en un contexto no polimórfico, a menudo se prefiere en situaciones en las que solo se necesita la información de clase, porque siempre es un procedimiento de tiempo constante , mientras que puede necesitar atravesar la red de derivación de clase de su argumento en tiempo de ejecución. [ cita requerida ] Algunos aspectos del objeto devuelto están definidos por la implementación, como , y no se puede confiar en que todos los compiladores sean coherentes.dynamic_cast<class_type>
typeid
dynamic_cast
std::type_info::name()
Los objetos de clase std::bad_typeid
se lanzan cuando la expresión para typeid
es el resultado de aplicar el operador unario * en un puntero nulo . Si se lanza una excepción para otros argumentos de referencia nulos depende de la implementación. En otras palabras, para que se garantice la excepción, la expresión debe tomar la forma typeid(*p)
donde p
está cualquier expresión que resulte en un puntero nulo.
Ejemplo
#include #include clase Persona { público : virtual ~ Persona () = predeterminado ; };clase Empleado : Persona pública {}; int main () { Persona persona ; Empleado empleado ; Persona * ptr = & empleado ; Persona & ref = empleado ; // La cadena devuelta por typeid :: name está definida por la implementación. std :: cout << typeid ( persona ). nombre () << std :: endl ; // Persona (conocida estáticamente en tiempo de compilación). std :: cout << typeid ( empleado ). nombre () << std :: endl ; // Empleado (conocido estáticamente en tiempo de compilación). std :: cout << typeid ( ptr ). nombre () << std :: endl ; // Persona * (conocido estáticamente en tiempo de compilación). std :: cout << typeid ( * ptr ). nombre () << std :: endl ; // Empleado (buscado dinámicamente en tiempo de ejecución // porque es la desreferencia de un // puntero a una clase polimórfica). std :: cout << typeid ( ref ). nombre () << std :: endl ; // Empleado (las referencias también pueden ser polimórficas) Persona * p = nullptr ; intente { typeid ( * p ); // Comportamiento no indefinido; lanza std :: bad_typeid. } atrapar (...) { } Persona & p_ref = * p ; // comportamiento indefinido: eliminación de referencias nula typeid ( p_ref ); // no cumple con los requisitos para lanzar std :: bad_typeid // porque la expresión para typeid no es el resultado // de aplicar el operador unario *. }
Salida (la salida exacta varía según el sistema y el compilador):
PersonaEmpleadoPersona*EmpleadoEmpleado
C ++ - dynamic_cast y Java cast
El dynamic_cast
operador en C ++ se utiliza para reducir una referencia o puntero a un tipo más específico en la jerarquía de clases . A diferencia de static_cast
, el objetivo de dynamic_cast
debe ser un puntero o una referencia a la clase . A diferencia static_cast
y de estilo C encasillado (donde se hizo que el registro tipo durante la compilación), un control de seguridad tipo se realiza en tiempo de ejecución . Si los tipos no son compatibles, se lanzará una excepción (cuando se trate de referencias ) o se devolverá un puntero nulo (cuando se trate de punteros ).
Un encasillado de Java se comporta de manera similar; Si el objeto que se está convirtiendo no es en realidad una instancia del tipo de destino y no se puede convertir en uno mediante un método definido por el lenguaje, se java.lang.ClassCastException
lanzará una instancia de . [5]
Ejemplo
Supongamos que alguna función toma un objeto de tipo A
como argumento y desea realizar alguna operación adicional si el objeto pasado es una instancia de B
, una subclase de A
. Esto se puede lograr de la dynamic_cast
siguiente manera.
#include #include #include #include usando el espacio de nombres std ;class A { public : // Dado que RTTI está incluido en la tabla de métodos virtuales, debería haber al menos // una función virtual. virtual ~ A () = predeterminado ; void MethodSpecificToA () { cout << "Se invocó el método específico para A" << endl ; } };clase B : public A { public : void MethodSpecificToB () { cout << "Se invocó el método específico para B" << endl ; } };void MyFunction ( A & my_a ) { try { // La transmisión será exitosa solo para objetos de tipo B. B & my_b = dynamic_cast < B &> ( my_a ); my_b . MethodSpecificToB (); } catch ( const bad_cast & e ) { cerr << "Excepción" << e . what () << "arrojado". << endl ; cerr << "El objeto no es de tipo B" << endl ; } }int main () { matriz < unique_ptr < A > , 3 > matriz_de_a ; // Matriz de punteros a la clase base A. array_of_a [ 0 ] = make_unique < B > (); // Puntero al objeto B. array_of_a [ 1 ] = make_unique < B > (); // Puntero al objeto B. array_of_a [ 2 ] = make_unique < A > (); // Puntero a un objeto. para ( int i = 0 ; i < 3 ; ++ i ) MiFunción ( * matriz_de_a [ i ]); }
Salida de consola:
Se invocó el método específico para BSe invocó el método específico para BExcepción std :: bad_cast lanzada.El objeto no es de tipo B
Se MyFunction
puede escribir una versión similar de con punteros en lugar de referencias :
anular MyFunction ( A * my_a ) { B * my_b = dynamic_cast < B *> ( my_a ); if ( my_b ! = nullptr ) my_b -> methodSpecificToB (); else std :: cerr << "El objeto no es de tipo B" << std :: endl ; }
Delphi / Object Pascal
En Object Pascal, el operador is
se usa para verificar el tipo de una clase en tiempo de ejecución . Prueba la pertenencia de un objeto a una clase determinada, incluidas las clases de antepasados individuales presentes en el árbol de jerarquía de herencia (por ejemplo, Button1 es una clase TButton que tiene antepasados: TWinControl → TControl → TComponent → TPersistent → TObject , donde este último es el antepasado de todas las clases). El operador as
se utiliza cuando un objeto debe tratarse en tiempo de ejecución como si perteneciera a una clase antecesora.
La unidad RTTI se utiliza para manipular la información del tipo de objeto en tiempo de ejecución. Esta unidad contiene un conjunto de clases que le permiten: obtener información sobre la clase de un objeto y sus antepasados, propiedades, métodos y eventos, cambiar valores de propiedad y llamar a métodos. El siguiente ejemplo muestra el uso del módulo RTTI para obtener información sobre la clase a la que pertenece un objeto, crearlo y llamar a su método. El ejemplo asume que la clase TSubject se ha declarado en una unidad denominada SubjectUnit.
utiliza RTTI , SubjectUnit ;procedimiento WithoutReflection ; var MySubject : TSubject ; comience MySubject : = TSubject . Crear ; prueba Subject . Hola ; finalmente Sujeto . Libre ; terminar ; terminar ;procedimiento WithReflection ; var RttiContext : TRttiContext ; RttiType : TRttiInstanceType ; Asunto : TObject ; comience RttiType : = RttiContext . FindType ( 'SubjectUnit.TSubject' ) como TRttiInstanceType ; Asunto : = RttiType . GetMethod ( 'Crear' ) . Invocar ( RttiType . MetaclassType , []) . AsObject ; prueba RttiType . GetMethod ( 'Hola' ) . Invocar ( Asunto , []) ; finalmente Sujeto . Libre ; terminar ; terminar ;
Ver también
Referencias
- ^ Sun Microsystems (2000). "Identificación del tipo de tiempo de ejecución" . Guía de programación de C ++ . Oracle . Consultado el 16 de abril de 2015 .
- ^ Bjarne Stroustrup (marzo de 1993). "Una historia de C ++: 1979-1991" (PDF) . Bjarne Stroustrup. pag. 50 . Consultado el 18 de mayo de 2009 .
- ^ "Trabajando con RTTI - RAD Studio" . docwiki.embarcadero.com . Consultado el 6 de junio de 2021 .
- ^ Estándar C ++ (ISO / IEC14882) sección 5.2.8 [expr.typeid], 18.5.1 [lib.type.info] - http://cs.nyu.edu/courses/fall11/CSCI-GA.2110- 003 / documentos / c ++ 2003std.pdf
- ^ http://docs.oracle.com/javase/8/docs/api/java/lang/ClassCastException.html
enlaces externos
- dynamic_cast operador en IBM Mac OS X Compilers
- dynamic_cast operador en MSDN