Casteando la clase base a la clase derivada cuando la clase derivada solo agrega funciones no virtuales.
Supongamos que tengo algo similar a esto:
class Base {
public:
Base(int value) : value(value) {}
int getValue() const { return value; }
private:
int value_;
};
class Derived : public Base {
public:
// Derived solo tiene funciones no virtuales. No se agregaron miembros de datos adicionales.
int getValueSquared() const { return value_ * value; }
};
Y hago lo siguiente:
Base* base = new Base(42);
Derived* derived = static_cast<derived*>(base);
std::cout << derived->getValueSquared() << std::endl;
Estrictamente hablando, esto es comportamiento indefinido (UB). Hablando en términos prácticos, funciona perfectamente.
Los miembros de datos reales de Base (por ejemplo, int value_
) deben ubicarse en las mismas posiciones tanto si el objeto es realmente un Base
como si es realmente un Derived
(de lo contrario, buena suerte al realizar una conversión a un tipo superior). Además, getValueSquared()
no forma parte de la huella de memoria real de una instancia de Derived
, por lo que no es como si estuviera “ausente” o no construido a partir del objeto Base en memoria.
Sé que el comportamiento indefinido es suficiente razón para no hacer esto, pero lógicamente parece que siempre funcionaría. Entonces, ¿por qué no?
Pregunto esto porque parece ser una peculiaridad interesante para discutir…no porque tenga intención de usarlo en producción.</derived*>
davy.ai
Aunque pueda funcionar en la práctica, acceder a un objeto de una clase derivada a través de un puntero a su clase base es un comportamiento indefinido en C++. El comportamiento de dicho código no puede ser predicho o garantizado por el estándar del lenguaje, y puede resultar en consecuencias inesperadas o peligrosas.
Aunque parezca lógico que el desplazamiento de los miembros de datos en las clases base y derivadas deba ser el mismo, no hay nada que garantice esto en el estándar. De hecho, hay escenarios en los que esto puede no ser cierto. Además, el hecho de que
getValueSquared()
no forme parte de la disposición de memoria de un objeto de tipoBase
significa que puede no construirse o inicializarse correctamente al ser accedido a través de un puntero aBase
.Por lo tanto, aunque pueda funcionar en algunos casos, no se recomienda confiar en esta peculiaridad y el código debe ser escrito de manera que se adhiera al comportamiento definido en el estándar del lenguaje.