42 Castings en C++

Hasta ahora hemos usado sólo el casting que existe en C, que vimos en el capítulo 9. Pero ese tipo de casting no es el único que existe en C++, de hecho, su uso está desaconsejado, ya que el por una parte los paréntesis se usan mucho en C++, además, este tipo de casting realiza conversiones diferentes dependiendo de cada situación. Se recomienda usar uno de los nuevos operadores de C++ diseñados para realizar esta tarea.

C++ dispone de cuatro operadores para realizar castings, algunos de ellos necesarios para realizar conversiones entre objetos de una misma jerarquía de clases.

Operador static_cast<>

La sintaxis de este operador es:

static_cast<tipo> (<expresión>);

Este operador realiza una conversión de tipo durante la compilación del programa, de modo que no crea más código durante la ejecución, descargando esa tarea en el compilador.

Este operador se usa para realizar conversiones de tipo que de otro modo haría el compilador automáticamente, por ejemplo, convertir un puntero a un objeto de una clase derivada a un puntero a una clase base pública:

#include <iostream>
using namespace std;

class Base {
  public:
   Base(int valor) : x(valor) {}
   void Mostrar() { cout << x << endl; }
   
  protected:
   int x;
};

class Derivada : public Base {
  public:
   Derivada(int ivalor, float fvalor) : 
      Base(ivalor), y(fvalor) {}
   void Mostrar() {
      cout << x << ", " << y << endl; 
   }
   
  private:
   float y;
};

int main() {
   Derivada *pDer = new Derivada(10, 23.3);
   Base *pBas;
   
   pDer->Mostrar(); // Derivada
   pBas = static_cast<Base *> (pDer);
   // pBas = pDer; // Igualmente legal, pero implícito
   pBas->Mostrar(); // Base
   
   delete pDer;
   return 0;
}

Otro ejemplo es cuando se usan operadores de conversión de tipo, como en el caso del capítulo 35, en lugar de usar el operador de forma implícita, podemos usarlo mediante el operador static_cast:

#include <iostream>
using namespace std;
 
class Tiempo {
  public:
   Tiempo(int h=0, int m=0) : hora(h), minuto(m) {}
   
   void Mostrar();
   operator int() {
      return hora*60+minuto;
   }
  private:
   int hora;
   int minuto;
};

void Tiempo::Mostrar() {
   cout << hora << ":" << minuto << endl;
}

int main() {
   Tiempo Ahora(12,24);
   int minutos;
   
   Ahora.Mostrar();
   minutos = static_cast<int> (Ahora);
   // minutos = Ahora; // Igualmente legal, pero implícito
   
   cout << minutos << endl;
   
   return 0;
}

Este operador se usa en los casos en que el programador desea documentar las conversiones de tipo implícitas, con objeto de aclarar que realmente se desean hacer esas conversiones de tipo.

Operador const_cast<>

La sintaxis de este operador es:

const_cast<tipo> (<expresión>);

Se usa para eliminar o añadir los modificadores const y volatile de una expresión.

Por eso, tanto tipo como expresión deben ser del mismo tipo, salvo por los modificadores const o volatile que tengan que aparecer o desaparecer.

Por ejemplo:

#include <iostream>
using namespace std;
 
int main() {
   const int x = 10;
   int *x_var;
   
   x_var = const_cast<int*> (&x); // Válido
   // x_var = &x; // Ilegal, el compilador da error
   *x_var = 14;   // Indefinido
  
   cout << *x_var << ", " << x << endl;
   
   return 0;
}

En el ejemplo vemos que podemos hacer que un puntero apunte a una constante, e incluso podemos modificar el valor de la variable a la que apunta. Sin embargo, este operador se usa para obtener expresiones donde se necesite añadir o eliminar esos modificadores. Si se intenta modificar el valor de una expresión constante, el resultado es indeterminado.

El ejemplo anterior, compilado en Dev-C++ no modifica el valor de x, lo cual es lógico, ya que x es constante.

Operador reinterpret_cast<>

La sintaxis de este operador es:

reinterpret_cast<tipo> (<expresión>);

Se usa para hacer cambios de tipo a nivel de bits, es decir, el valor de la "expresión" se interpreta como si fuese un objeto del tipo "tipo". Los modificadores const y volatile no se modifican, permanecen igual que en el valor original de "expresión". Este tipo de conversión es peligrosa, desde el punto de vista de la compatibilidad, hay que usarla con cuidado.

Posibles usos son conseguir punteros a variables de distinto tipo al original, por ejemplo:

#include <iostream>
#include <iomanip>
using namespace std;
 
int main() {
   int x = 0x12dc34f2;
   int *pix = &x;
   unsigned char *pcx;
   
   <b>pcx = reinterpret_cast<unsigned char *> (pix);</b>
   
   cout << hex << " = "
      << static_cast<unsigned int> (pcx[0]) << ", "
      << static_cast<unsigned int> (pcx[1]) << ", "
      << static_cast<unsigned int> (pcx[2]) << ", "
      << static_cast<unsigned int> (pcx[3]) << endl;
   
   return 0;
}

La salida tiene esta forma:

12dc34f2 = f2, 34, dc, 12