С++ множественное наследование наследования

У меня есть класс:

class Base;

Также у меня есть интерфейс

class Interface;

Далее я создаю класс

class Derived : public Base, public Interface;

Если у меня есть Base *object = new Derived;

Как я могу отбрасывать object до Interface? (конечно, если я знаю, чем объект на самом деле является производным классом)

EDIT:

Я пробовал dynamic_cast и static_cast (не скомпилирован). Поэтому позвольте мне объяснить проблему немного больше:

У меня есть:

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 {...}

Объект имеет свойство bool touchable_. Если это правда, тогда объект реализует интерфейс ITouchResponder.

Когда я его использую:

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 является методом object:

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

У меня плохая ошибка в xcode.

Но если я делаю Object : public ITouchResponder, все работает нормально. Что я делаю неправильно?

Полный класс объекта:

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_;
};

Ответы

Ответ 1

Если Base имеет функцию virtual (пусть даже это virtual деструктор), то:

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

В противном случае используйте

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

Обратите внимание, что если Base не имеет виртуальной функции, то dynamic_cast НЕ компилируется. В dynamic_cast, только источник должен быть полиморфным объектом, чтобы его компилировать, и если назначение не является полиморфным, тогда dynamic_cast вернет нулевой указатель:

Предположим, что A и B являются полиморфными типами, а C является не полиморфным, тогда

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;

Ответ 2

Если вы точно знаете, что объект имеет класс Derived - используйте static_cast, в противном случае используйте dynamic_cast и проверьте результат.

Ответ 3

Вы можете использовать dynamic_cast<Derived*>(object) для этого, и если при успешном произнесении вы получите возвращенный Derived*, иначе приведение возвращает NULL. Вы используете этот тип приведения, если Base является полиморфным типом, т.е. содержит виртуальную функцию, и если это не так, вы можете вместо этого использовать static_cast<Derived*>.

Ваша проблема в том, что у вас есть класс Object, который не наследует от вашего интерфейса ITouchResponder, и поэтому ваш dynamic_cast из Object на ваш интерфейс недействителен. Вы можете делать только dynamic_cast на классах, которые наследуют друг от друга, что вся цель полиморфизма, так, как вы предложили в своем примере, ваш класс Object должен наследоваться публично из вашего интерфейса.

По существу, вы делаете upcast здесь, ITouchResponder *res = dynamic_cast<ITouchResponder*>(this); от производного до базового интерфейса, однако ваш Derived действительно не вытекает из вашего интерфейса, поэтому почему он не работает.

Ответ 4

Существует несколько способов, но они не оказывают одинакового воздействия:

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

Используйте это, если во время компиляции вы знаете, что он должен быть правильного типа. Это не сработает во время выполнения, если это невозможно, но во время компиляции.

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

Используйте это, если вы не уверены, и хотите, чтобы среда выполнения автоматически проверяла, возможно ли это. Если это так, вы получите правильный указатель, если нет, вы получите nullptr. Знайте, что проверки dynamic_cast < > являются дорогостоящими по времени, поэтому многие люди иногда используют ассоциативные контейнеры с указателями типа type как ключ, где это возможно, потому что проверка менее дорога, но на самом деле это зависит от контекста. Чтобы получить дополнительную информацию, найдите ключевое слово typeid.

Теперь есть и способ C, но это не рекомендуется, потому что не понятно, что именно будет генерироваться компилятором. По крайней мере, с этими предыдущими способами вы точно знаете, как должен себя вести код. Поэтому я не буду описывать это.