EVAP2

OPERADOR TYPEID
Sinopsis
La palabra clave typeid identifica un operador con el que puede obtenerse el tipo de objetos y expresiones en tiempo de ejecución  . Permite comprobar si un objeto es de un tipo particular, y si dos objetos son del mismo tipo. Este tipo de identificación en tiempo de ejecución es conocida abreviadamente como RTTI .
Sintaxis
typeid( expresion )
typeid( nombre-de-tipo )
Ejemplo
cout << typeid(int).name() << endl;
int x = 10, z = 20;
cout << typeid(x).name() << endl;
const type_info & ref = typeid(int);
cout << ref.name() << endl;
Descripción
Como puede verse, el operador typeid acepta dos variantes sintácticas que adoptan la forma de función. En la primera, el operando es una expresión que adopta el papel de argumento de la función; una llamada a esta función devuelve una referencia a un objeto de tipo constante, type_info. Como veremos a continuación , este objeto describe el tipo del operando . Ejemplo:
int x = 10, y = 20;
const type_info & tipo1 = typeid(x+z);
class C { int x; } c1;
const type_info & tipo2 = typeid(c1);

La segunda forma sintáctica permite que el operando sea un nombre-de-tipo. Entonces typeid devuelve la referencia a un objeto type_info para ese tipo. El operador puede utilizarse tanto con tipos fundamentales como con tipos definidos por el usuario. Ejemplo:
const type_info & tipo3 = typeid(float);
const type_info & tipo4 = typeid(C);

Generalmente esta función-operador se utiliza para obtener el tipo de objetos referenciados mediante punteros o referencias. Situaciones como:
tipoX  Objeto;
tipoX* puntero = &Objeto;
tipoX& referencia = Objeto;
....
cout << "El objeto es de tipo: " << typeid(*puntero).name();
cout << "El objeto es de tipo: " << typeid(referencia).name();
Observe que la expresión  typeid(puntero) devuelve tipoX*
Nota: la salida no está normalizada, de forma que depende del compilador
El operador typeid no  puede ser sobrecargado
  El objeto type_info
Como se ha dicho, typeid devuelve una referencia a un objeto de tipo constante que describe el tipo del operando. Este objeto es la instancia de una clase denominada type_info que dispone de los operadores ye que pueden utilizarse para comprobar si dos objetos son del mismo tipo. Esto significa que cuando utilizamos una expresión como:
if (typeid( X ) == typeid( Z ))
el operador == es una versión específica (sobrecargada) del operador igualdad para la clase type_info. Esta clase también dispone de sendos métodos: name() y before() a disposición del usuario. Recuerde que para utilizar la clasetype_info es necesario incluir el fichero de cabecera <typeinfo>.
Lo anterior puede resumirse diciendo que el resultado del operador es la referencia a un objeto de una clase, y que este objeto solo puede ser utilizado mediante dos métodos y dos operadores para comparación. Para más información al respecto ver: 
Cuando el operador typeid se intenta aplicar a un operando que es la deferencia de un puntero nulo se lanza la excepción bad_typeid (ver advertencias y notas que siguen).
Es importante señalar que aunque puede usarse con cualquier objeto, este operador se ha pensado para obtener el tipo de objetos polimórficos. Por supuesto no tiene mucho sentido una expresión como:
cout << "El tipo int es de tipo: " << typeid(int).name();

A pesar de lo que señalan la mayoría de los manuales y la bibliografía, typeid solo calcula en tiempo de ejecución  el tipo de objetos polimórficos. En otro caso (si el objeto no es polimórfico) los calcula en  tiempo de complicación . Puede comprobarse con un sencillo ejemplo:
#include <iostream>
#include <typeinfo>
using namespace std;

int main() {       // ===========
  try {
     int* ptr = 0;
     const type_info & refx = typeid(*ptr);
     cout << "El tipo es: " << refx.name() << endl;
  }
  catch(...) {
     cout << "Recibida excepción." << endl;
     return 0;
  }
  cout << "NO Recibida excepción !!!!" << endl;
  return 0;
}

Contra lo señalado en el párrafo anterior sobre el lanzamiento de la excepción bad_typeid , el programa produce la misma salida con todos los compiladores en que he probado (Borland C++ 5.5;  MS Visual C++ V 6.0, y Cpp version 2.95.2 GNU/Linux):
El tipo es: int
NO Recibida excepción !!!!
La razón es la ya apuntada: typeid solo calcula en tiempo de ejecución el tipo de objetos polimórficos (que tienen al menos un método virtual En este caso, en que el tipo de *ptr es siempre int, no se lanza la excepción a pesar de que se trata del valor de un puntero nulo.

Una versión  del programa anterior que pusiera de manifiesto el lanzamiento de la excepción bad_typeid, sería el siguiente:
#include <iostream>
#include <typeinfo>
using namespace std;

  struct X {
    virtual void f() =0;
  };
  struct Y : X { void f() { } };

int main() {       // ===========
  try {
    X* ptr = 0;
    const type_info& refx = typeid(*ptr);
    cout << "El tipo es: " << refx.name() << endl;
  }
  catch(...) {
    cout << "Recibida excepción." << endl;
    return 0;
  }
  cout << "NO Recibida excepción !!!!" << endl;
  return 0;
}
En este caso la salida es:
Recibida excepción.

El ejemplo siguiente muestra el uso del operador typeid, y de los dos métodos before() y name() de la clasetype_info.
#include <iostream.h>
#include <typeinfo.h>

class A { };
class B : A { };

void main() {    // ==========
   char C;
   float X;

   // Uso de type_info::operator==() para hacer la comparación
   if (typeid( C ) == typeid( X ))
      cout << "C y X son del mismo tipo." << endl;
   else
      cout << "C y X son de distinto tipo." << endl;

   // Uso de literales true y false para hacer la comparación
   cout << typeid(int).name();
   cout << " antes que " << typeid(double).name() << ": " <<
        (typeid(int).before(typeid(double)) ? true : false) << endl;
   cout << typeid(double).name();
   cout << " antes que " << typeid(int).name() << ": " <<
        (typeid(double).before(typeid(int)) ? true : false) << endl;
   cout << typeid(A).name();
   cout << " antes " << typeid(B).name() << ": " <<
        (typeid(A).before(typeid(B)) ? true : false) << endl;
}
Salida:
C y X son de distinto tipo.
int antes que double: 0
double antes que int: 1
A antes que B: 1

El siguiente ejemplo muestra la utilización de typeid para ilustrar una fuente de posibles errores en la declaración de punteros:
#include <iostream.h>
#include <typeinfo.h>

void main() {           // ======================
  int* pt1, pt2;        // L.5:  Ojo posible error !!
  if (typeid( pt1 ) == typeid( pt2 ))
    cout << "pt1 y pt2 son del mismo tipo." << endl;
  else
    cout << "pt1 y pt2 son de distinto tipo." << endl;

  int * pt3; int* pt4;  // L.11: Ok. más seguro
  if (typeid( pt3 ) == typeid( pt4 ))
    cout << "pt3 y pt4 son del mismo tipo." << endl;
  else
    cout << "pt3 y pt4 son de distinto tipo." << endl;
}
Salida:
pt1 y pt2 son de distinto tipo.
pt3 y pt4 son del mismo tipo.
Nota: el resultado obtenido con expresiones del tipo de L.5 depende de la implementación, pero ha sido idéntico con los compiladores Borland C++  5.5 y MS Visual C++ 6.0, por lo que en la declaración de punteros son preferibles expresiones como L.11.

A continuación se muestra otro ejemplo en el que el operador typeid es utilizado para seleccionar el constructor adecuado a un objeto. Suponemos que el tipo de objeto a manejar se presenta como una cadena alfanumérica obtenida de algún modo. Por ejemplo, la consulta a una base de datos, introducida por teclado o leída de un fichero, etc.
void HandleType(char* typeName) {
  if (strcmp(typeName, typeid(Clase1).name())==0) {
    Clase1 obj;
    obj.display();
  }
  else if (strcmp(typeName, typeid(Clase2).name())==0) {
    Clase2 obj;
    obj.display();
  }
  else if (strcmp(typeName, typeid(Clase3).name())==0) {
    Clase3 obj;
    obj.display();
  }
  ...      // etc.
}
RTTI
El mecanismo C++ que permite determinar en tiempo de ejecución el tipo de un objeto, se conoce generalmente por su acrónimo inglés RTTI ("Run time type identification"). Este sistema es una parte importante del mecanismo de comprobación de tipos del lenguaje y permite la identificación incluso cuando el objeto solo es accesible mediante un puntero o referencia [3]. Por ejemplo, el sistema hace posible convertir un puntero a clase-base virtual en puntero a clase derivada. Recordar que para realizar modelados en tiempo de ejecución debe utilizarse el operadordynamic_cast .
Este mecanismo también permite comprobar si un objeto es de un tipo particular y cuando dos objetos son del mismo o distinto tipo. Esto puede hacerse con el operador typeid que da título al presente capítulo.
El mecanismo RTTI forma parte de un sistema más amplio de funciones y/o clases de la Librería Estándar C++ que proporcionan determinadas funcionalidades de tiempo de ejecución.

Deshabilitar el mecanismo RTTI
La utilización del mecanismo RTTI produce cierta sobrecarga en tiempo de ejecución, por lo que la mayoría de compiladores disponen de una opción para habilitarlo o deshabilitarlo a voluntad.  En concreto, C++Builder dispone de una opción de compilación el comando -RT-, que provoca que no se genere código que permite la identificación de tipos en tiempo de ejecución. Por defecto su estado es activado (ON). Por su parte, el compilador GNU gcc dispone de la opción -fno-rtti cuya finalidad es análoga.  Recordar también que esta opción puede ser incluida de forma particular en la declaración de clases.
Si se activa la opción de habilitar limpieza total de destructores para el manejo de excepciones  también se necesita activar esta opción. También es necesaria si se desea utilizar el operador dynamic_cast, que se basa en este mecanismo para comprobar si el modelado está permitido o no.
INICIO
Esta observación es válida solamente para el compilador Borland C++: cuando el operando de typeid es una clase o referencia a un objeto Delphi, devuelve el tipo estático (de tiempo de compilación) en vez del de tiempo de ejecución. Ejemplo:
static const char *TypeIdName(TObject *c) {
   return typeid(*c).name();
}
//  The button’s caption is set to TObject, not TButton.
void __fastcall TForm1::Button1Click(TObject *Sender) {
   Button1->Caption = TypeIdName(Button1);
}


No hay comentarios:

Publicar un comentario