c++ multiple inheritance casting

11,989

Solution 1

If Base has virtual function (even be it virtual destructor), then:

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

Else, use

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

Note that if Base doesn't have virtual function, then dynamic_cast will NOT compile. In dynamic_cast, only the source has to be a polymorphic object, in order to compile, and if the destination isn't polymorphic, then dynamic_cast will return null pointer:

Suppose A and B are polymorphic type, and C is non-polymorphic, then

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;

Solution 2

If you know for sure that the object is of Derived class - use static_cast, otherwise use dynamic_cast and check the result.

Solution 3

You can use dynamic_cast<Derived*>(object) for that, and if the cast succeeds you'll get a Derived* returned, else the cast will return NULL. You use this type of cast if Base is a polymorphic type, ie. contains a virtual function, and if this is not the case, you can use a static_cast<Derived*> instead.

Your problem is that you have an Object class that does not inherit from your interface ITouchResponder and therefore your dynamic_cast from Object to your interface is invalid. You can only do a dynamic_cast on classes that inherit from one another, that's the whole purpose of polymorphism, so like you suggested in your example, your Object class should inherit publicly from your interface.

Essentially, you're doing an upcast here, ITouchResponder *res = dynamic_cast<ITouchResponder*>(this); from Derived to Base Interface, however your Derived doesn't really derive from your interface, so that's why it doesn't work.

Solution 4

There are several ways but they don't have the same impact :

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

Use this one if you know at compile time that it should be of the correct type. It will not fail at runtime if it's not possible but will at compile time.

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

Use this if you're not sure and want the runtime to automatically check if it's possible. If it is you'll get a valid pointer, if not you'll get a nullptr. Know that dynamic_cast<> checks are costly in time performance so a lot of people sometimes use associative containers with type_info pointers as key, where possible, because the check is less costly, but really it depends on the context. To get more infos, look for the typeid keyword.

Now, there is also the C way of doing it but it's not recommanded because it's not clear what exactly will the compiler generated. At least with those previous ways you know exactly how the code should behave. So I'll not describe it.

Share:
11,989
Andrew
Author by

Andrew

Software Development Engenier, Microsoft http://ru.linkedin.com/pub/andrew-vorobyev/48/2b2/bab

Updated on June 17, 2022

Comments

  • Andrew
    Andrew about 2 years

    I have a class:

    class Base;
    

    Also I have an interface

    class Interface;
    

    Next i'm creating a class

    class Derived : public Base, public Interface;
    

    If I have Base *object = new Derived;

    How can i cast object to Interface ? (of course if i know than object is actually a derived class)

    EDIT:

    I've tried dynamic_cast and static_cast (not compiled). So let me explain the problem a bit more:

    I have:

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

    Object have a bool touchable_ property. If it's true then object is implementing ITouchResponder interface.

    When I use it:

    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 is a method of Object :

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

    I have bad excess error in xcode.

    But if i make Object : public ITouchResponder everything works fine. What am i doing wrong ?

    Full object class:

    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_;
    };
    
  • PlasmaHH
    PlasmaHH almost 13 years
    That only works when the Base is polymorphic (e.g. by having a virtual function). When you are totally sure, a static_cast is ok, but then again you should wonder why your being sure is not expressed in the code as using the type right away. Or by using virtual functions or the visitor pattern.
  • RedX
    RedX almost 13 years
    He says he knows it's a Derived so he should use static_cast<Derived>(object) since it incurs in much less overhead.
  • Nawaz
    Nawaz almost 13 years
    dynamic_cast will not compile if the base has not virtual function.
  • sharptooth
    sharptooth almost 13 years
    @Nawaz: True, but the compiler will emit a nice error message, yet static_cast will lead to UB if the object if of the wrong type and the compiler won't say a word. So here the key is not having virtual functions but knowing the type of the object.
  • Matthieu M.
    Matthieu M. almost 13 years
    It would be even better if you precised that dynamic_cast may return a null pointer, also since the main improvement of your post is precising limitations, it may be worth noting that crossing virtual inheritance relationship requires dynamic_cast (static_cast cannot).
  • Tony The Lion
    Tony The Lion almost 13 years
    Show us your 'Object' base class, does it have virtual functions inside?
  • Andrew
    Andrew almost 13 years
    @Tony: Why can't i use Object obj = new Ball() and cast it to ITouchResponder? The Ball is ITouchResponder