Operador dynamic_cast<>
La sintaxis de este operador es:
dynamic_cast<tipo> (<objeto>);
Se usa para hacer cambios de tipo durante la ejecución. Y se usa la base de datos formada por las estructuras type_info que vimos antes.
Este operador sólo puede usarse con objetos polimórficos, cualquier intento de aplicarlo a objetos de tipos fundamentales o agregados o de clases no polimórficas dará como resultado un error.
Hay dos modalidades de dynamic_cast, una usa punteros y la otra referencias:
class Base { // Clase base virtual ... }; class Derivada : public Base { // Clase derivada ... }; // Modalidad de puntero: Derivada *p = dynamic_cast<Derivada *> (&Base); // Modalidad de referencia: Derivada &refd = dynamic_cast<Derivada &> (Base); |
Lo que el programa intentará hacer, durante la ejecución, es obtener bien un puntero o bien una referencia a un objeto de cierta clase base en forma de clase derivada, pero puede que se consiga, o puede que no.
Esto es, en cierto modo, lo contrario a lo que hacíamos al usar polimorfismo. En lugar de obtener un puntero o referencia a la clase base a partir de la derivada, haremos lo contrario: intentar obtener un puntero o referencia a la clase derivada a partir de la clase base.
Volvamos a nuestro ejemplo de Personas.
Vamos a añadir un miembro "sueldo" a la clase "Empleado", y la modificaremos para poder inicializar y leer ese dato.
También crearemos una función "LeerSueldo" que aceptará como parámetro un puntero a un objeto de la clase "Persona":
#include <iostream> #include <cstring> using namespace std; class Persona { // Virtual public: Persona(char *n) { strcpy(nombre, n); } virtual void VerNombre() = 0; // Virtual pura protected: char nombre[30]; }; class Empleado : public Persona { public: Empleado(char *n, float s) : Persona(n), sueldo(s) {} void VerNombre() { cout << "Emp: " << nombre << endl; } void VerSueldo() { cout << "Salario: " << sueldo << endl; } private: float sueldo; }; class Estudiante : public Persona { public: Estudiante(char *n) : Persona(n) {} void VerNombre() { cout << "Est: " << nombre << endl; } }; void VerSueldo(Persona *p) { if(Empleado *pEmp = dynamic_cast<Empleado *> (p)) pEmp->VerSueldo(); else cout << "No tiene salario." << endl; } int main() { Persona *Pepito = new Estudiante("Jose"); Persona *Carlos = new Empleado("Carlos", 1000.0); Carlos->VerNombre(); VerSueldo(Carlos); Pepito->VerNombre(); VerSueldo(Pepito); delete Pepito; delete Carlos; return 0; } |
La función "VerSueldo" recibe un puntero a un objeto de la clase base virtual "Persona". Pero a priori no sabemos si el objeto original era un empleado o un estudiante, de modo que no podemos prever si tendrá o no sueldo. Para averiguarlo hacemos un casting dinámico a la clase derivada "Empleado", y si tenemos éxito es que se trata efectivamente de un empleado y mostramos el salario. Si fracasamos, mostramos un mensaje de error.
Pero esto es válido sólo con punteros, si intentamos hacerlo con referencias tendremos un serio problema, ya que no es posible declarar referencias indefinidas. Esto quiere decir que, por una parte, no podemos usar la expresión del casting como una condición en una sentencia if. Por otra parte, si el casting fracasa, se producirá una excepción, ya que se asignará un valor nulo a una referencia durante la ejecución:
void VerSueldo(Persona &p) { Empleado rEmp = dynamic_cast<Empleado &> p; ... |
Por lo tanto tendremos que usar manejo de excepciones (que es el tema del siguiente capítulo), para usar referencias con el casting dinámico:
void VerSueldo(Persona &p) { try { Empleado rEmp = dynamic_cast<Empleado &> p; rEmp.VerSueldo(); } catch (std::bad_cast) { cout << "No tiene salario." << endl; } } |
Castings cruzados
Cuando tenemos una clase producto de una derivación múltiple, es posible obtener punteros o referencias a una clase base a partir de objetos o punteros a objetos de otra clase base, eso sí, es necesario usar polimorfismo, no podemos usar un objeto de una clase base para obtener otra. Por ejemplo:
#include <iostream> using namespace std; class ClaseA { public: ClaseA(int x) : valorA(x) {} void Mostrar() { cout << valorA << endl; } virtual void nada() {} // Forzar polimorfismo private: int valorA; }; class ClaseB { public: ClaseB(float x) : valorB(x) {} |
Comentarios