2011-09-14 26 views
14

tengo una clase:C++ herencia múltiple fundición

class Base; 

También tengo una interfaz

class Interface; 

siguiente estoy creando una clase

class Derived : public Base, public Interface; 

Si tengo Base *object = new Derived;

¿Cómo puedo emitir object a Interface? (Por supuesto, si sé que el objeto es en realidad una clase derivada)

EDIT:

He intentado dynamic_cast y static_cast (no compilado). Así que vamos a explicar el problema un poco más:

que tengo:

class Object {...} 

class ITouchResponder 
{ 
public: 
    virtual bool onTouchBegan(XTouch *touch) = 0; 
    virtual void onTouchMoved(XTouch *touch) = 0; 
    virtual void onTouchEnded(XTouch *touch) = 0; 
}; 

class Ball : public Object, public ITouchResponder {...}; 

class TentacleSensor : public Object, public ITouchResponder {...} 

objeto tiene una propiedad bool touchable_. Si es cierto, entonces el objeto implementa la interfaz ITouchResponder.

cuando lo uso:

bool Level::onTouchBegan(XTouch *touch) 
{ 
    ... 
    ITouchResponder *responder = callback.nearestTouchable(); 
    if (responder) 
    { 
     if (responder->onTouchBegan(touch)) 
     { 
      if (responder != ball_) 
      { 
       touch->setUserData(responder); 
      } 
     } 
    } 

    return true; 
} 

ITouchResponder *QueryCallback::nearestTouchable() const 
{ 
    for (list<Object*>::const_iterator it = objects_.begin(); it != objects_.end(); ++it) 
    { 
     if ((*it)->isTouchable()) return (*it)->asTouchResponder(); 
    } 
    return 0; 
} 

asTouchResponder es un método de Object:

ITouchResponder * Object::asTouchResponder() 
    { 
     assert(touchable_); 
     ITouchResponder *res = dynamic_cast<ITouchResponder*>(this); 
     assert(res); 
     return res; 
    } 

tengo mal el exceso de error en Xcode.

Pero si hago Object : public ITouchResponder todo funciona bien. Qué estoy haciendo mal ?

completa clase de objeto:

class Object// : public ITouchResponder 
{ 
public: 
    struct Def 
    { 
     Def() 
     { 
      level = 0; 
      world = 0; 
      touchable = false; 
      acceptsContacts = false; 
      body = 0; 
      node = 0; 
     } 
     Level *level; 
     b2World *world; 
     bool touchable; 
     bool acceptsContacts; 
     b2Body *body; 
     XNode *node; 
    }; 

    Object(const Def &def); 
    virtual ~Object(); 

    virtual void update(float dt); 
    bool isTouchable() const {return touchable_;} 

    void addDependantObject(Object *object); 
    void removeDependantObject(Object *object); 

    virtual void objectWillBeRemoved(Object *object) {} //this function is automatically called to every dependant object when object is removed 

    virtual XVec2 position() const; 
    virtual float rotation() const; 

    bool acceptsContacts() const {return acceptsContacts_;} 

    b2Body *body() const {return body_;} 

    Level *level() const {return level_;} 
    b2World *world() const {return world_;} 

    ITouchResponder *asTouchResponder(); 
    /* 
    virtual bool onTouchBegan(XTouch *touch) { 
     return false; 
    } 
    virtual void onTouchMoved(XTouch *touch) 
    { 
    } 
    virtual void onTouchEnded(XTouch *touch) 
    { 
    }*/ 

protected: 
    Level *level_; 
    b2World *world_; 
    bool touchable_; 
    bool acceptsContacts_; 

    XNode *node_; 
    b2Body *body_; 

    list<Object*> dependantObjects_; 
}; 
+0

(derived *) object – vivek

+2

@vivek No utilice c cast en código C++. Hay una razón por la cual se introdujo la nueva sintaxis. – RedX

+0

Idealmente, su interfaz pública virtual pura podría utilizarse sin la necesidad de invocación mediante métodos de llamada en el puntero de interfaz que se anulan en la clase derivada para hacer lo que sea apropiado. – AJG85

Respuesta

8

Si Base tiene virtual función (aunque sea virtual destructor), entonces:

Derived *pDerived = dynamic_cast<Derived *>(object); 

Si no, utilizar

Derived *pDerived = static_cast<Derived *>(object); 

Tenga en cuenta que si Base no tiene función virtual, entonces dynamic_cast NO compilará. En dynamic_cast, única la fuente tiene que ser un objeto polimórfico, con el fin de compilar, y si el destino no es polimórfico, entonces dynamic_cast volverá puntero nulo:

Supongamos A y B son de tipo polimórfico, y C es no polimórficos, entonces

A *pA = dynamic_cast<A*>(new C()); //error - source is not polymorphic! 

A *pA = dynamic_cast<A*>(new B()); //ok 
if (pA == 0) 
     cout << "pA will be null if B is not derived from A" << endl; 

C *pC = dynamic_cast<C*>(new B()); //ok 
if (pC == 0) 
     cout << "pC must be null" << endl; 
+0

Sería incluso mejor si precisa que 'dynamic_cast' puede devolver un puntero nulo, ya que la mejora principal de su publicación es precisar las limitaciones, puede valer la pena señalar que el cruce de la herencia virtual requiere una relación 'dynamic_cast' (' static_cast' no puede). –

+0

@Matthieu: Lo he hecho en la respuesta editada. – Nawaz

+0

@Nawaz: vea mi edición – Andrew

4

Usted puede utilizar dynamic_cast<Derived*>(object) para eso, y si la conversión se realiza correctamente que obtendrá un Derived* devuelto, de lo contrario el elenco devolverá NULL. Usas este tipo de yeso si Base es de tipo polimórfico, es decir. contiene una función virtual, y si este no es el caso, puede usar un static_cast<Derived*> en su lugar.

Su problema es que usted tiene una clase Object que no hereda de su interfaz de ITouchResponder y por lo tanto su dynamic_cast de Object a su interfaz no es válido. Solo puede hacer un dynamic_cast en clases que heredan entre sí, ese es el propósito del polimorfismo, así que como sugirió en su ejemplo, su clase Object debería heredar públicamente de su interfaz.

Esencialmente, usted está haciendo un upcast aquí, ITouchResponder *res = dynamic_cast<ITouchResponder*>(this); desde Derivado a la Interfaz base, sin embargo, su derivada realmente no se deriva de su interfaz, por eso no funciona.

+3

Eso solo funciona cuando la Base es polimórfica (por ejemplo, al tener una función virtual). Cuando esté totalmente seguro, un static_cast está bien, pero nuevamente debe preguntarse por qué está seguro de que no está expresado en el código como usar el tipo de inmediato. O mediante el uso de funciones virtuales o el patrón de visitante. – PlasmaHH

+1

Dice que sabe que es un derivado, por lo que debería usar 'static_cast (object)' ya que incurre en mucha menos sobrecarga. – RedX

+0

Tony: por favor, mira mi edición – Andrew

8

Si está seguro de que el objeto es de clase Derived, use static_cast, de lo contrario use dynamic_cast y compruebe el resultado.

+1

'dynamic_cast' no compilará si la base no tiene función virtual. – Nawaz

+2

@Nawaz: Es cierto, pero el compilador emitirá un bonito mensaje de error, pero static_cast llevará a UB si el objeto es del tipo incorrecto y el compilador no dirá una palabra. Entonces aquí la clave no es tener funciones virtuales, sino conocer el tipo de objeto. – sharptooth

+0

@sharptooth: consulte mi edición – Andrew

3

Hay varias maneras, pero no tienen el mismo impacto:

Derived* derived = static_cast<Derived*>(object); 

Utilice este si sabe en tiempo de compilación que debe ser del tipo correcto. No fallará en el tiempo de ejecución si no es posible, pero lo hará en tiempo de compilación.

Derived* derived = dynamic_cast<Derived*>(object); 

Utilice esto si no está seguro y desea que el tiempo de ejecución compruebe automáticamente si es posible. Si es así obtendrás un puntero válido, si no obtendrás un nullptr. Tenga en cuenta que las comprobaciones de dynamic_cast <> son costosas en el rendimiento del tiempo, por lo que muchas personas a veces utilizan contenedores asociativos con punteros type_info como clave, siempre que sea posible, porque la verificación es menos costosa, pero realmente depende del contexto. Para obtener más información, busque la palabra clave typeid.

Ahora, también existe la forma C de hacerlo, pero no se recomienda porque no está claro qué exactamente generará el compilador. Al menos con esas formas previas, usted sabe exactamente cómo debe comportarse el código. Entonces no lo describiré.

+0

@please vea mi edición – Andrew