Search code examples
c++multiple-inheritance

How to 'set' object to a particular base class in multiple inheritence?


I'm trying to inherit from two base classes in C++, that have same named functions. I want an object of the class to 'belong' to the particular base class, is it possible to do so?

I have tried virtual base class (does not suit my particular case) and also tried using scope resolution operator with failure.

I want to do this just by using a single class if it's possible.

I have provide the code;

#include <SFML\Graphics.hpp>

class entity :public sf::CircleShape, public sf::RectangleShape {
public: 
    entity(int radius = 0) { setRadius(radius); }
    entity(sf::Vector2f size) { setSize(size); }
    float xspeed =0, yspeed =0;
};
entity ball(6);
entity block(10, 10);
window.draw(block);   // ambiguity error as both have base class sf::drawable
window.draw(ball);    // and draw function accepts sf::drawable

The problem is that I want "ball" just to inherit from sf::CircleShape and not from sf::RectangleShape.

I expected using scope resolution operator would refer to the circleshape class or I might just be using it wrong.

Edit: The problem I'm trying to solve is that I want to draw "ball" and a rectangular block on to the window, and when I try to do that I'm presented with an ambiguity error as they both are drawable (i.e. both sf::RectangleShape and sf::CircleShape inherit from sf::drawable)

Edit(2): The inheritence diagram

So the function window.draw(); accepts any object of sf::drawable as a parameter. But as I inherit both circleshape and rectangle shape to entity I get an ambiguity error saying base class is ambiguous.

I know this is diamond of death situation however using virtual base classes is not possible as they are part of the SFML library and I do not want to modify them


Solution

  • Problem analysis

    class entity :public sf::CircleShape, public sf::RectangleShape
    

    This declaration says that every entity is simultaneously a CircleShape and a RectangleShape. This does not seem to be what you want. You seem to want an entity that can be either a circle or a rectangle. So this inheritance is not the right strategy.

    Given that you are adding horizontal and vertical speeds, it seems like you are trying to add a basic animation to elements that can be drawn. Animation is an abstract concept that builds upon drawing (an animation must be drawn, but not all drawings must be animated). Specific moving shapes would build on top of the animation, so your ideal inheritance would be more like the following:

    circle -> entity -> drawable
    

    However, there are a few drawbacks. One drawback is that this forces all circles to be animated entities, when some circles might simply be drawn in one spot. Plus, the circle and drawable classes are from a library, so you cannot mess with that particular inheritance scheme.

    I can think of two reasonable alternatives off the top of my head. (By the way, "entity" is not a great name for your class, but I'll stick with it for consistency. You should come up with a more descriptive name for the class. It'll be good practice, as naming things is sometimes one of the hardest parts of writing code. :) )

    Entity as a base class

    You could define entity as a base class, then define a new class for each shape.

                 --> sf::CircleShape --> sf::Drawable
                /
    MyCircle --<
                \
                 --> entity
    

    This can work, but it is not great if entity needs to invoke functions defined in Drawable. Not impossible: a side-cast can make it possible to invoke Drawable's functions. It's just that you then have to account for the case where the side-cast fails, as the compiler cannot catch that.

    Shapes as members

    What I would probably do is give up inheritance altogether. Instead of trying to say that your entity is a circle (inheritance), I would go with the approach that your entity has a circle (membership). One benefit of this approach is that it is not hard to extend this to saying that your entity has multiple shapes, all moving at the same speed. I will, though, stick to a single shape for now.

    The difficulty with this approach is that you don't know what shape the entity should have -- should it have a circle or a rectangle? Fortunately, this is the sort of thing polymorphism deals with nicely.

    class entity {
        std::unique_ptr<sf::Drawable> shape; // <-- Polymorphism to the rescue!
        float xspeed = 0.0, yspeed = 0.0;
    
    public:
        // Construct a circle.
        entity(int radius = 0) : shape(std::make_unique<sf::CircleShape>(radius)) {}
    
        // Construct a rectangle.
        entity(sf::Vector2f size) : shape(std::make_unique<sf::RectangleShape>(size)) {}
    
        // Something will go here to support drawing.
    };
    

    To support drawing, there are (at least) two options. If it is appropriate to have an entity be a drop-in replacement for a Drawable, it might be reasonable to define an implicit conversion.

        operator const sf::Drawable &() const { return *shape; }
    

    If an implicit conversion is not desirable, marking it explicit is an option.

    If the only time you need to use an entity as a Drawable is when you call window.draw(), you might want to instead give entity a draw method that takes window as a parameter.

        void draw(sf::RenderTarget & target, sf::RenderStates states) const
        { target.draw(*shape, states); }
    

    This makes the Drawable available to RenderTarget::draw() while keeping it out of sight (clutter reduction) at other times.