Virtual methods with MI

Hey, I'm here again for questions about multiple inheritance and virtual methods. Well actually, I don't think MI affects this, I'll try to stick to the point.

In a physics engine I'm working on, I have a class called solid. All objects of this class have hitboxes and can collide with others. I have the following methods:
1
2
3
4
5
6
7
8
void testCollision(something begin, something end);
/* This method takes a container's begin and end iterators to
   test if the object collides with any other object of the list
   of all the solids currently in the game area. Each time there
   is a collision, it calls collide(other) and other.collide(*this) */

virtual bool collide(solid& other);
/* This method always returns false and does nothing */


This class will be inherited by another class which will have overloads for a few specific collisions. For example:
1
2
3
4
5
6
class player : public solid{
public:
    bool collide(projectile& other);
    bool collide(enemy& other);
    bool collide(wall& other);
};


My question is quite simple actually. If I have a loop which calls testCollision() with all elements in the list of all solids (a list of pointers to solids to be exact) and there is a collision between the player and a projectile, will testCollision call player::colide(projectile& other) or will it call solid::collide(solid& other). And in any case, did I understand how to use the virtual keyword? If I'm right, it should call the player::colide method if it's there for the specific type, else it will call the solid::colide which only returns 0, ignoring collision.
Last edited on
I do not think you have overridden collide(solid &other) because the types you specified functions for in player are not direct solids. I also think you should declare player's functions here as:

virtual bool collide(projectile &other) override;

When collide is called and dynamic dispatch is activated; it means it can not automatically downcast correctly to any other type; you'll have to specify a function:

virtual bool collide(solid &other) override;

in player so that you can try downcasting the "other" reference and call the correct implementation. You can also copy the collide functions from player into the solid class in order to get your desired result, but you may implicitly up-cast in testCollision, and thus call the collide(solid &other) virtual function.

To answer your question, I think your function will call solid's collide function and not dispatch anything further because nothing matches the signature down the inheritance chain.
You should probably use a mediator to resolve collisions.
http://sourcemaking.com/design_patterns/mediator

With the above structure, something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>

struct solid
{
    virtual ~solid() {}
    virtual bool collide( const solid& ) const { return false ; }
};

struct projectile : solid { /* ... */ };
struct enemy : solid { /* ... */ };
struct wall : solid { /* ... */ };

struct player : public solid
{
    virtual bool collide( const solid& other ) const override
    {
        if( const projectile* p = dynamic_cast< const projectile* >(&other) )
            return collide( *p ) ;

        if( const enemy* e = dynamic_cast< const enemy* >(&other) )
            return collide( *e ) ;

        if( const wall* w = dynamic_cast< const wall* >(&other) )
            return collide( *w ) ;

        return solid::collide(other) ;
    }

    private:
        virtual bool collide( const projectile& ) const
        { return std::cout << "player::collide(projectile)\n" ; }

        virtual bool collide( const enemy& ) const
        { return std::cout << "player::collide(enemy)\n" ; }

        virtual bool collide( const wall& ) const
        { return std::cout << "player::collide(wall)\n" ; }
};

int main ()
{
    projectile p ;
    enemy e ;
    wall w ;
    const solid* objects[] = { &e, &w, &p } ;

    player this_player ;
    const solid& this_soild = this_player ;
    for( const solid* s : objects ) this_soild.collide(*s) ;

}

http://coliru.stacked-crooked.com/a/779e521e8912fe2a

We can use a lookup table mechanism to make the code shorter and faster;
but using the mediator pattern is probably the better option.
I see that you always put the solid& other as const. I guess that makes more sense since we call other.collide(*this). I don't need to do much in terms of physics, it's a space shooter so collisions are mostly there to deal damage and destroy entities. However, the current object will be modified, so the method itself isn't const.

I guessed it wouldn't be as magical as I put it in my first post, but I didn't know about dynamic casting, which does pretty much what I've been looking for.

But in this case, would it be simpler or quicker to simply hold a variable representing the type of solid? Oh no, because a projectile would have a getDamage() method required by the player, which an enemy wouldn't have... Dynamic cast seems to be the solution here. Well thanks a lot, you're always the one finding the solution to my problems here!
I was quite careless; we should give a different name to the private collide functions to which we forward.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
struct player : public solid
{
    // TODO: remove the const qualifiers

    virtual bool collide( const solid& other ) const override
    {
        if( const projectile* p = dynamic_cast< const projectile* >(&other) )
            return do_collide( *p ) ;

        if( const enemy* e = dynamic_cast< const enemy* >(&other) )
            return do_collide( *e ) ;

        if( const wall* w = dynamic_cast< const wall* >(&other) )
            return do_collide( *w ) ;

        return solid::collide(other) ;
    }

    private:
        virtual bool do_collide( const projectile& ) const
        { return std::cout << "player::collide(projectile)\n" ; }

        virtual bool do_collide( const enemy& ) const
        { return std::cout << "player::collide(enemy)\n" ; }

        virtual bool do_collide( const wall& ) const
        { return std::cout << "player::collide(wall)\n" ; }
};



> would it be simpler or quicker to simply hold a variable representing the type of solid?

We do not need it when RTTI is already there.

In any case, maintaining such a variable with correct semantics is not as trivial as it would appear at first sight.
(For instance, the implicitly declared copy constructor would not do at all).
Topic archived. No new replies allowed.