Constructores de conversión y casteos.

Iniciado por SARGE553413, 22 Julio 2013, 16:56 PM

0 Miembros y 1 Visitante están viendo este tema.

SARGE553413

Hola a todos, intentaré ser lo mas conciso posible.

He leído y comprobado que en java no se puede hacer un cast de una superclase a una subclase, así que me propuse intentarlo en C++:

Al intentarlo observo que con static_cast, reinterpret_cast y al estilo C, compila y ejecuta, pero efectivamente los nuevos atributos de la clase derivada no son inicializados (yo pensaba que con estos castings se haría llamada al constructor apropiado, pero no es así). Obviamente, no funciona como debería.

¿Como puedo hacer para poder castear la superclase a una subclase con dynamic_cast y que todo se inicialice bien?

PD.
No se si se podrá pero, ¿sobrecargando dynamic_cast de alguna manera?

He intentado sobrecargar el operador '=' para que al hacer la asignación todo funcione bien. El problema ha sido que lo he hecho con referencias, para conseguir lo que quiero ¿tienen que ser punteros a la fuerza porque necesito polimorfismo?:
(El operador '=' hará cosas distintas si hago 'SuperClase=SuperClase' que si hago 'SuperClase=SubClase')



eferion

Para poder hacer un cast a una subclase y que el cast sea correcto, la instancia afectada tiene que ser, necesariamente, de la subclase.

Me explico:

Código (cpp) [Seleccionar]

Class A
{ };

Class B : public A
{ };

int main ( ... )
{
  A* puntero1 = new A( );
  A* puntero2 = new B( );

  // este caso retornará 0 porque puntero1 no es de la subclase B
  B* clase_b1 = dynamic_cast< B* >( puntero1 );

  // este caso retornará un puntero válido
  B* clase_b2 = dynamic_cast< B* >( puntero2 );
}


Luego, dado que para que el cast funcione correctamente, la instancia original ha de ser de la subclase ( o de una subclase de la subclase ), necesariamente has tenido que crear una instancia de la subclase y, al hacer esa creación, se han tenido que inicializar necesariamente todos los miembros de la clase.

Si esto no es así es porque se te ha olvidado inicializar algo en el constructor.

SARGE553413

#2
Gracias por la respuesta.

Luego es imposible tener una superclase A, una clase derivada B, construir un objeto como clase A, y castearlo a clase B, es decir:


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

int main(){
  A *a=new A();

  B *b=dynamic_cast<B *>(a);
 //Esto da error.

 B *b2=static_cast<B *>(a);
//Esto no da error, el problema es que los atributos que incorpora 'B' no son    
//inicializados, a no ser que lo estubiesen ya.

//Tampoco hay manera de llamar a un constructor automaticamente al hacer ese
//static_cast para que las variables que incorpora 'B' sean inicializadas, ¿cierto?  

}        


Muchas Gracias


eferion

dynamic_cast hace chequeos que verifican que, efectivamente, se puede hacer el cast a la clase destino. Esto solo es posible cuando la instancia apuntada es de la clase destino o de alguna clase hija... en caso contrario, el cast retornará un puntero nulo.

static_cast símplemente comprueba que el cast (a pelo) es válido. Es un chequeo muucho más sencillo que el dinámico y, por ello, más propenso a errores... por ejemplo que te deje hacer lo que estabas comentando.

Está claro que si tu tienes una superclase A... creas una instancia de esta clase y luego intentas hacer un cast a su subclase B... los miembros de esta subclase no pueden estar bien. En primer lugar porque en c++, a diferencia de en java, los miembros no se inicializan por defecto.

El constructor de A sólo sabe inicializar la parte que es propia de A... pero no sabe nada de la parte de B... luego esa parte siempre va a quedar con valores aleatorios.

Tu piensa que en el fondo, una clase no deja de ser x bytes en memoria y una serie de funciones que hacen uso de posiciones específicas de esa memoria... el concepto de clase solo tiene sentido para tí... la máquina no entiende de eso.

SARGE553413

Gracias de nuevo por la respuesta.
Eso mismo me planteba yo ( la vision de la clase en memoria) por eso preguntaba si si habia alguna manera de llamar al constructor de la clase B durante el casteo para construirla bien pero veo que no,ok.

Sin embargo ahora mi pregunta es que pasa si la superclase es abstracta. Supongamos que tengo una superclase A y dos que heredan  C y D. Si defino un metodo que devuelve un puntero a A, si que puedo castear una superclase a una de sus clases derivadas sin problema. Esto es porq dentro del metodo he construido el objeto como una de laa clases derivadas ( necesariamente porque  esabstracta) ¿correcto?


Y si dentro deo metodo conatruyo la clase como B y tras delver el puntero mo casteo a C?

eferion

Cita de: SARGE553413 en 24 Julio 2013, 14:25 PM
Eso mismo me planteba yo ( la vision de la clase en memoria) por eso preguntaba si si habia alguna manera de llamar al constructor de la clase B durante el casteo para construirla bien pero veo que no,ok.

Efectivamente no se puede. Los constructores son llamados únicamente al crear la clase... una vez que ya está creada los constructores no son invocados de nuevo. Además, tu piensa que las subclases, como norma general, necesitan más memoria que las superclases... y la memoria ha de ser secuencial. Una vez ya has hecho una reserva de memoria no puedes garantizar que una nueva reserva se realice inmediatamente después... luego no podrías crear correctamente la nueva clase.

Cita de: SARGE553413 en 24 Julio 2013, 14:25 PM
Sin embargo ahora mi pregunta es que pasa si la superclase es abstracta. Supongamos que tengo una superclase A y dos que heredan  C y D. Si defino un metodo que devuelve un puntero a A, si que puedo castear una superclase a una de sus clases derivadas sin problema. Esto es porq dentro del metodo he construido el objeto como una de laa clases derivadas ( necesariamente porque  esabstracta) ¿correcto?

A ver, voy a intentar explicarlo con algo físico... para que sea más gráfico.

Tu imagínate que tienes la superclase Animal... y de ella heredan las clases Gato y Perro.

Si tu ( imagínate que eres una especie de dios ), creas un Gato... a ese gato lo puedes identificar como gato o como animal... pero nunca llegará a ser un perro ( tus poderes no dan para tanto ).

Si en vez de un gato creas un perro pasará exactamente lo mismo, el perro podrá ser tratado como un perro o como un animal, pero nunca como un gato.

El polimorfismo lo que te permite es tener punteros genéricos ( de tipo superclase ), que en realidad representen a entidades heredadas... pero este mecanismo no permite experimentos de mutación genética que permitan convertir un perro en un gato.

Si tu intentas llevar a cabo esta aberración de la naturaleza te pasará lo siguiente:

Código (cpp) [Seleccionar]

class Animal
{
};

class Perro: public Animal
{
};

class Gato : public Animal
{
};

void main( )
{
  Animal* gato = new Gato( );
  Animal* perro = new Perro( );

  Gato* gato1 = dynamic_cast< Gato* >( gato ); // ok
  Gato* gato2 = static_cast< Gato* >( gato ); // ok pero menos seguro

  Perro* perro1 = dynamic_cast< Perro* >( perro ); // ok
  Perro* perro2 = static_cast< Perro* >( perro ); // ok pero menos seguro

  Gato* aberracion1 = dynamic_cast< Gato* >( perro ); // devuelve 0.
  Gato* aberracion2 = static_cast< Gato* >( perro ); // funciona pero la aplicación va a dar resultados incorrectos o casques.

  Perro* aberracion3 = dynamic_cast< Perro* >( gato); // devuelve 0.
  Perro* aberracion4 = static_cast< Perro* >( gato); // te funciona pero la aplicación va a dar resultados incorrectos o casques.
}