Introducción: ¿por qué C++ no perdona los errores de conversión de tipos?
¡Hola, amigo! Si alguna vez has escrito en C++, seguro te has encontrado con una situación en la que el compilador se queja por una discrepancia de tipos. Y entonces, probablemente, recuerdas el viejo y querido C-style cast: (int)x. ¿Funciona? Sí. ¿Es seguro? No. ¿Profesional? Aún menos.
En C++ hay cuatro operadores de conversión incorporados, y cada uno resuelve su propia tarea: static_cast, dynamic_cast, const_cast y reinterpret_cast. Usarlos correctamente significa escribir código que no solo funciona, sino que también es comprensible para otros desarrolladores. Y además, no falla en tiempo de ejecución con errores misteriosos.
En este tutorial, analizaremos cada cast con ejemplos reales, entenderemos cuándo aplicar cada uno y cuándo es mejor no hacer ninguna conversión. ¡Allá vamos!
static_cast: tu amigo confiable para conversiones comunes
static_cast es el cast más popular y seguro. Se utiliza para conversiones explícitas entre tipos relacionados: tipos numéricos, punteros hacia arriba/abajo en la jerarquía de herencia (sin verificación en tiempo de ejecución), enumeraciones a enteros y viceversa.
¿Cuándo usar static_cast?
- Conversión de
intadouble(y viceversa). - Conversión de punteros dentro de una misma jerarquía de clases (upcast — seguro, downcast — se necesita estar seguro).
- Conversión de
void*a un tipo específico. - Llamada a constructores explícitos u operadores de conversión.
Ejemplo:
double pi = 3.14159;int approx = static_cast<int>(pi); // 3
class Base { public: virtual ~Base() = default; };class Derived : public Base { public: void foo() {} };
Base* b = new Derived;Derived* d = static_cast<Derived*>(b); // OK, pero sin verificaciónd->foo();delete b;Importante: static_cast no realiza verificación de tipos en tiempo de ejecución. Si estás seguro de que el objeto es realmente de ese tipo, úsalo. Si no, mira dynamic_cast.
dynamic_cast: downcast seguro con verificación
dynamic_cast es el único cast que funciona solo con clases polimórficas (debe tener al menos una función virtual). Verifica en tiempo de ejecución si se puede convertir el puntero/referencia de forma segura. Si no, devuelve nullptr (para punteros) o lanza std::bad_cast (para referencias).
¿Cuándo usar dynamic_cast?
- Cuando no estás seguro de que el objeto sea exactamente del tipo derivado.
- Al trabajar con contenedores heterogéneos (por ejemplo, vector de Base*).
- En patrones de diseño (por ejemplo, Visitor).
Ejemplo:
class Base { public: virtual ~Base() = default; };class Derived : public Base { public: void specific() {} };class Other : public Base {};
vector<Base*> objects = {new Derived, new Other, new Derived};
for (Base* obj : objects) { Derived* d = dynamic_cast<Derived*>(obj); if (d) { d->specific(); // seguro solo para Derived }}Desventaja: dynamic_cast es más lento que static_cast porque requiere RTTI (Run-Time Type Information). No abuses de él en secciones críticas para el rendimiento.
const_cast: cuando necesitas eliminar la constante (¡pero con cuidado!)
const_cast es el único cast que puede eliminar (o agregar) cv-calificadores (const, volatile). Suena genial, pero en la práctica se usa raramente y solo en casos extremos.
¿Para qué sirve? A veces recibes una referencia constante a un objeto, pero necesitas llamar a una función antigua que no está marcada como const, aunque no modifica el objeto. O al trabajar con bibliotecas de C.
Ejemplo (correcto):
void printInt(const int* p) { // antiguo