En programación informática , la información de tipo en tiempo de ejecución o identificación de tipo en tiempo de ejecución ( RTTI ) [ 1 ] es una característica de algunos lenguajes de programación (como C++ , [ 2 ] Object Pascal y Ada [ 3 ] ) 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 puede estar disponible para todos los tipos o solo para los tipos que la tienen explícitamente (como es el caso de Ada). La información de tipo en tiempo de ejecución 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 incluyó información de tipo en tiempo de ejecución, porque pensaba que este mecanismo se usaba con frecuencia de forma incorrecta. [ 4 ]
Descripción general
En C++, RTTI se puede usar para realizar conversiones de tipo seguras usando el dynamic_cast<T>operador y para manipular información de tipo en tiempo de ejecución usando el typeidoperador y std::type_infola clase, que a su vez almacena la información de tiempo de ejecución. En Object Pascal, RTTI se puede usar para realizar conversiones de tipo seguras con el asoperador, probar la clase a la que pertenece un objeto con el isoperador y manipular información de tipo en tiempo de ejecución con clases contenidas en la RTTIunidad [ 5 ] (es decir, clases: TRttiContext, TRttiInstanceType, etc.). En Ada, los objetos de tipos etiquetados también almacenan una etiqueta de tipo, que permite la identificación del tipo de estos objetos en tiempo de ejecución. El inoperador se puede usar para probar, en tiempo de ejecución, si un objeto es de un tipo específico y se puede convertir de forma segura a él. [ 6 ]
RTTI solo está disponible para clases polimórficas , es decir, clases que tienen al menos un método virtual . En la práctica, esto no supone una limitación, ya que las clases base deben tener un destructor virtual para permitir que los objetos de las clases derivadas realicen la limpieza adecuada si se eliminan de un puntero base.
Algunos compiladores tienen indicadores para deshabilitar RTTI. El uso de estos indicadores puede reducir el tamaño total de la aplicación, lo que los hace especialmente útiles cuando se dirigen a sistemas con una cantidad limitada de memoria. [ 7 ]
typeidoperador C++
La typeidpalabra clave se utiliza para determinar la clase de un objeto en tiempo de ejecución. Devuelve una referencia al std::type_infoobjeto, que existe hasta el final del programa. [ 8 ] El uso de typeid, en un contexto no polimórfico, suele preferirse a dynamic_cast<T>en situaciones donde solo se necesita la información de la clase, porque typeidsiempre es un procedimiento de tiempo constantedynamic_cast , mientras que puede necesitar recorrer la red de derivación de clases de su argumento en tiempo de ejecución. Algunos aspectos del objeto devuelto son definidos por la implementación, como , y no se puede confiar en que sean consistentes entre compiladores.std::type_info::name()
Se lanza una std::bad_typeidexcepcióntypeid() cuando la expresión es el resultado de aplicar el *operador unario a un puntero nulo . El lanzamiento de una excepción para otros argumentos de referencia nula depende de la implementación. En otras palabras, para que se garantice la excepción, la expresión debe tener la forma typeid(*p)donde pes cualquier expresión que dé como resultado un puntero nulo.
El typeidoperador, si se usa en un contexto donde std::type_infono es visible, está mal informado. Está definido en el encabezado <typeinfo>(o módulostd ).
C++26 ofrece mayor soporte para la introspección de tipos reflexiva a través del tipo std::meta::info, pero se realiza en tiempo de compilación . [ 9 ]
Ejemplo
importar std ;using std :: bad_typeid ; using std :: type_info ;clase Persona { público : virtual ~ Persona () = predeterminado ; };clase Empleado : público Persona { // ... };int main () { Persona persona ; Empleado empleado ; Persona * ptr = & empleado ; Persona & ref = empleado ;type_info personType = typeid ( person ); type_info employeeType = typeid ( employee ); type_info ptrType = typeid ( ptr ); type_info refType = typeid ( ref ); // La cadena devuelta por std::type_info::name() está definida por la implementación.std :: println ( "{}" , personType.name ( ) ); // Persona (conocida estáticamente en tiempo de compilación).std :: println ( "{}" , employeeType.name ( )); // Empleado (conocido estáticamente en tiempo de compilación).std :: println ( "{}" , ptrType.name ( ) ); // Persona* (conocida estáticamente en tiempo de compilación).std :: println ( "{}" , refType.name ( )); // Empleado // (se busca dinámicamente en tiempo de ejecución // porque es la desreferenciación de un // puntero a una clase polimórfica). std :: println ( "{}" , typeid ( ref ) .name ()); // Empleado (las referencias también pueden ser polimórficas)Persona * p = nullptr ; try { typeid ( * p ); // No es un comportamiento indefinido; lanza std::bad_typeid. } catch ( const bad_typeid & e ) { std :: println ( stderr , "Excepción capturada: {}" , e . what ()); }Persona & ref2 = * p ; // Comportamiento indefinido: desreferenciación de null// No cumple los requisitos para lanzar std::bad_typeid // porque la expresión para typeid no es el resultado // de aplicar el operador unario *. type_info ref2Type = typeid ( ref2 ); }Salida (la salida exacta varía según el sistema y el compilador ):
Persona Empleado Persona* Empleado Empleado
Reparto de C++ dynamic_casty Java
El dynamic_castoperador en C++ se utiliza para convertir una referencia o puntero a un tipo más específico en la jerarquía de clases . A diferencia de static_cast, el destino de dynamic_castdebe ser un puntero o referencia a la clase . A diferencia de static_casty la conversión de tipos al estilo C (donde la verificación de tipos ocurre durante la compilación), se realiza una verificación de seguridad de tipos 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 ).
Una conversión de tipo en Java se comporta de manera similar; si el objeto que se está convirtiendo no es realmente una instancia del tipo de destino y no se puede convertir a uno mediante un método definido por el lenguaje, java.lang.ClassCastExceptionse lanzará una instancia de. [ 10 ]
Ejemplo
Supongamos que una función toma como argumento un objeto de tipo Basey desea realizar alguna operación adicional si el objeto pasado es una instancia de Derived, una subclase de Base. Esto se puede hacer usando dynamic_castde la siguiente manera.
importar std ;using std :: array ; using std :: bad_cast ; using std :: unique_ptr ;clase Base { private : void specificToBase () const { std :: println ( "Se invocó el método específico para Base" ); } public : // Dado que RTTI está incluido en la tabla de métodos virtuales, debería haber al menos una función virtual. virtual ~ Base () = default ; };clase Derivada : pública Base { pública : void specificToDerived () const { std :: println ( "Se invocó el método específico para B" ); } };void myFunction ( Base & base ) { try { // La conversión solo será exitosa para objetos de tipo B. Derived & derived = dynamic_cast < Derived &> ( base ); derived . specificToDerived (); } catch ( const bad_cast & e ) { std :: println ( stderr , "Excepción {} lanzada." , e . what ()); std :: println ( "El objeto no es de tipo Derived" ); } }int main ( int argc , char * argv [ ] ) { // Arreglo de punteros a la clase base A. array<unique_ptr<Base>, 3> arrayOfBase = { std::make_unique<Derived> ( ) ; // Puntero al objeto Derived . std :: make_unique <Derived> ( ) ; // Puntero al objeto Derived . std :: make_unique <Base> ( ) ; // Puntero al objeto Base . }for ( Base b : arrayOfBase ) { myFunction ( * b ); } return 0 ; }Salida de la consola:
Se invocó un método específico para Derived. Se invocó un método específico para Derived. Se ha lanzado una excepción std::bad_cast. El objeto no es de tipo Derivado
myFunctionSe puede escribir una versión similar utilizando punteros en lugar de referencias :
void myFunction ( Base * base ) { Derived * derived = dynamic_cast < Derived *> ( base );if ( derived ) { derived -> specificToDerived (); } else { std :: println ( stderr , "El objeto no es de tipo Derived" ); } }Objeto Pascal, Delphi
En Object Pascal y Delphi , el operador isse utiliza para comprobar el tipo de una clase en tiempo de ejecución. Comprueba si un objeto pertenece a una clase determinada, incluidas las clases de los ancestros individuales presentes en el árbol de jerarquía de herencia (por ejemplo, Button1 es una clase TButton que tiene los siguientes ancestros: TWinControl → TControl → TComponent → TPersistent → TObject , donde este último es el ancestro de todas las clases). El operador asse utiliza cuando un objeto debe tratarse en tiempo de ejecución como si perteneciera a una clase ancestra.
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 permiten obtener información sobre la clase de un objeto y sus ancestros, propiedades, métodos y eventos, así como modificar los valores de las propiedades y llamar a métodos. El siguiente ejemplo muestra cómo usar el módulo RTTI para obtener información sobre la clase a la que pertenece un objeto, crearlo y llamar a sus métodos. El ejemplo presupone que la clase TSubject se ha declarado en una unidad llamada SubjectUnit.
utiliza RTTI , SubjectUnit ;procedimiento SinReflexión ; var MySubject : TSubject ; inicio MySubject : = TSubject.Create ; intentar Subject.Hello ; finalmente Subject.Free ; fin ; fin ;procedimiento WithReflection ; var RttiContext : TRttiContext ; RttiType : TRttiInstanceType ; Subject : TObject ; begin RttiType := RttiContext.FindType ( ' SubjectUnit.TSubject' ) as TRttiInstanceType ; Subject : = RttiType.GetMethod ( ' Create ' ) . Invoke ( RttiType.MetaclassType , [ ] ) . AsObject ; try RttiType.GetMethod ( ' Hello ' ) . Invoke ( Subject , [ ] ) ; finally Subject.Free ; end ; end ;Véase también
Referencias
- ↑ Sun Microsystems (2000). "Identificación de tipo en tiempo de ejecución" . Guía de programación de C++ . Oracle . Consultado el 16 de abril de 2015 .
- ↑ "Biblioteca de soporte de lenguaje [ support.rtti ] " . eel.is . Consultado el 13-07-2021 .
- ↑ "Programación orientada a objetos" . learn.adacore.com . Consultado el 13 de julio de 2021 .
- ^ 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 .
- ↑ English, John (22 de febrero de 2002). "Capítulo 15". Ada 95: El arte de la programación orientada a objetos . Recuperado el 13 de julio de 2021 .
- ↑ "Evitando RTTI y soporte para -fno-rtti en Arm Compiler 6" . Arm Developer . Consultado el 13 de julio 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/documents/c++2003std.pdf
- ↑ cppreference.com. "Encabezado de biblioteca estándar <meta /> (C++26)" . cppreference.com . cppreference.com . Consultado el 17 de mayo de 2026 .
- ↑ "ClassCastException (Java Platform SE 8)" .
Enlaces externos
dynamic_castOperador en IBM Mac OS X Compilersdynamic_castoperador en MSDN
- C++
- Clase (programación informática)
- Tipos de datos
- Comparación de lenguajes de programación