C ++ множественное наследование - PullRequest
15 голосов
/ 14 сентября 2011

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

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

Ответы [ 4 ]

8 голосов
/ 14 сентября 2011

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

8 голосов
/ 14 сентября 2011

Если 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;
4 голосов
/ 14 сентября 2011

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

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

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

3 голосов
/ 14 сентября 2011

Есть несколько способов, но они не имеют одинакового воздействия:

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

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

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

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

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

...