Problema con herencia múltiple[C++][?]

Iniciado por xalcoz, 8 Enero 2017, 18:22 PM

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

xalcoz

Tengo un fallo que no logro entender en este programa de herencia múltiple de clases abstractas. El compilador me acepta el casting sin problemas, pero a la hora de ejecutar el programa, me imprime por pantalla 2 veces la operación A en vez de primero la A y luego la B, a pesar de que la variable B no debería de tener acceso a esa función, ¿alguien sabe por qué?


#include <iostream>
using namespace std;

class A{
public:
    virtual void opA() = 0;
};

class B{
public:
    virtual void opB() = 0;
};

class AB: public A, public B{
public:
    void opA(){
        cout<<"Operacion A"<<endl;
    }
    void opB(){
        cout<<"Operacion B"<<endl;
    }
};


int main(){
    A* variableA = new AB();
    B* variableB = reinterpret_cast<B*>(variableA);

    variableA->opA();
    variableB->opB();

    delete variableA;
    return 0;
}

ivancea96

#1
Para convertir un puntero a un objeto de una clase a otra clase, tienes 3 casts posibles:

  • static_cast: En el momento de compilar, comprueba si lo puede convertir. En tu caso, estás convirtiendo un A* a un B*. Como A* no hereda de B*, dará error. (Aunque sea un AB*, es una variable A* lo que estás convirtiendo a B*)
  • dynamic_cast: En vez de comprobar a la hora de compilar, lo hace mientras se ejecuta. Este va a comprobar si se puede hacer el cast, y si no se puede, lanzará una excepción (En este caso se puede, y no tendrás problema).
  • reinterpret_cast: Este cast no hace comprobaciones. Se limita a tratar la secuencia de bytes del objeto como si de otro objeto se tratase. En este caso, eso no es suficiente, y por eso falla.

Aquí, tu opción es dynamic_cast. Otra opción (bastante mejor a mi parecer), es utilizar un objeto AB*:
Código (cpp) [Seleccionar]
#include <iostream>
using namespace std;

class A{
public:
   virtual void opA() = 0;
};

class B{
public:
   virtual void opB() = 0;
};

class AB: public A, public B{
public:
   void opA(){
       cout<<"Operacion A"<<endl;
   }
   void opB(){
       cout<<"Operacion B"<<endl;
   }
};


int main(){
   AB* ab = new AB();
   A* variableA = ab;
   B* variableB = ab;

   variableA->opA();
   variableB->opB();

   delete ab;
   return 0;
}


No necesitarás hacer casts (el cast implícito funciona correctamente en este caso), y además tendrás la certeza de que los tipos son siempre válidos.

En tu ejemplo, estás convirtiendo un A* a un B*. Viendo el código, sabemos que eso funciona, pero no es realmente válido, pues esas clases no tienen relación.

Este último método es preferible ante el dynamic_cast porque te dará error en tiempo de compilación (preferible a una excepción), y la transformación es más rápida (dynamic_cast hace comprobaciones que podemos obviar).

Cuidado con reinterpret_cast a la hora de trabajar con clases. Mejor evitarlo, especialmente cuando se utiliza herencia múltiple.
Un caso de uso de reinterpret_cast podría ser:
Código (cpp) [Seleccionar]
int n = 456;
char* bytesDeN = reinterpret_cast<char*>(&n);



EDITO: Y sobre el por qué hace lo que dices, pues: reinterpret_cast en ese caso no hará un cast correcto, y el comportamiento será indefinido. Probablemente, como opA es la primera función, B la coja como si fuera opB. En cualquier caso, este comportamiento es indefinido y no es relevante. Simplemente, debe evitarse.

xalcoz

¡Gracias por la respuesta! Ya me estaba volviendo loco porque no entendía qué estaba pasando, me ha funcionado el código perfectamente con las dos soluciones que me has proporcionado :D