0

I'm currently creating a game in SFML. To put it simply, I have an Object class that has all the common features for all objects. Player and Enemy class inherit from Object. There is also an ObjectManager that has a list of all Object and updates and draws them etc.

// using SFML shapes

class Player : public Object
{
    public:
        sf::CircleShape playerShape;
};

class Enemy : public Object
{
    public:
        sf::Sprite enemyShape;
};

class Object
{
    public:
        void move(...) { // move the shape, but the shapes are only found in the derived }
        void draw(...) { // same problem as above, etc... }

};

class ObjectManager
{
    private:
        std::map<int, std::shared_ptr<Object>> gameObjectMap; // id, object
    public:
        void updateAll(...) {
            // loop over all objects and update them
            for (auto itr = gameObjectMap.begin(); itr != gameObjectMap.end(); ++itr) {
                itr->second->move(...);
                itr->second->draw(...);
            }
        }
};

Above you can see that Object::move() cannot be used because Object does not know about the SFML shapes in the derived classes. Therefore you could make the function Object::move() pure virtual but this means that you'd need to write implementations in the derived classes for any shape specific function that you need to use, this also includes any attributes about the shape, such as its position sf::Shape::getPosition(), etc. So this approach does not scale well.

An alternative that I thought about would be to have the Object class as a class template

template<typename T>
class Object
{
    protected:
        T object;
    public:
        void move(...) { object.move(...); }
        T getObject() { return object; }
};

class Player : public Object<sf::CircleShape>
{ ... };

However this means that ObjectManager now must hold class templates Object in the map, and I'm not sure how that would work?

What's the best way to handle this situation?

Alexander
  • 410
  • 5
  • 14
  • 3
    That's what virtual functions are for. What you have is practically a textbook example. – Igor Tandetnik Aug 19 '17 at 00:42
  • **TL; DR;** provide interfaces. – user0042 Aug 19 '17 at 00:44
  • Minimal or misdirected topic research, but a well-written question. – user4581301 Aug 19 '17 at 00:48
  • Another option could be to implement the move method in your base class using the base class for shapes rather than whatever implementation specific shape you end up using. – Matt Aug 19 '17 at 00:48
  • 1
    Possible duplicate of [Why do we need Virtual Functions in C++?](https://stackoverflow.com/questions/2391679/why-do-we-need-virtual-functions-in-c) – user4581301 Aug 19 '17 at 00:49
  • I'm aware of virtual functions, however in this particular instance it appeared like a bad choice, because you'd be writing many functions for the sole reason of just retrieving properties of the shapes in the derived classes into the base class - functions that are already part of the Shape class, so you'd just be duplicating existing function definitions. As opposed to some form of restructuring of the code to get the shape to the base class, avoiding writing all those functions which would be equivalent for all derived classes with shapes of type sf::Shape anyway. – Alexander Aug 19 '17 at 01:06

2 Answers2

0

how about this:

class Object
{
    virtual auto move() -> void = 0;
};


template <class Shape>
class Shape_object : Object
{
   Shape shape;

   auto move() -> void override
   {
      // implementation
   }
};
// you can have specializations for each shape type
// if you can't use the generic one


class Player : public Shape_object<sf::CircleShape>
{
   //
};

class Enemy : public Shape_object<sf::Sprite>
{
   //
};
bolov
  • 72,283
  • 15
  • 145
  • 224
  • What about all the functions in Object, should Player and Enemy also inherit from Object or would Shape_object inherit from Object, else the functions are not overriden? – Alexander Aug 19 '17 at 01:55
  • @Alexander sorry, a slip up. Shape_object should obviously inherit from Object. Edited. – bolov Aug 19 '17 at 09:34
0
template<typename T>
class Object {
    // ...
};

If you really consider using a template class, you should use the CRTP (aka Static Polymorphism):

template<typename Derived>
class Object {
public:
    /* virtual */ auto move() -> void {
 // ^^^^^^^^^^^^^ Hooray! No more vtable \o/ (but wait ...)
        static_cast<Derived*>(this)->doMove(); // Fails to compile, if 
                                               // Derived isn't inheriting from
                                               // Object<Derived> or Derived 
                                               // doesn't implement doMove().
    };
};

The drawback of that pattern is that everything must be resolved at compile time. Runtime injections of interface implementations (e.g. via plugins) won't work with that pattern well.


You could leave a thin layer though to make virtual destruction and such work properly:

struct ObjectBase {
    virtual ~ObjectBase() {} // << that's enough
};

template<typename Derived>
class Object : public ObjectBase {
    // ...
}
user0042
  • 7,917
  • 3
  • 24
  • 39