1

I'm having a bit of trouble overriding a function from a base class.

Here's my GameObject class:

namespace GameEngine {
    class GameObject {
    public:
        virtual void render() {
            std::cout << "Render Game Object" << std::endl;
        }
    };
}

And here's my Player class:

class Player : public GameEngine::GameObject {
public:
    void render() {
        std::cout << "Render Player" << std::endl;
    }
};

In MainComponent.cpp I have a Vector of GameObjects, and I loop through them to render them

Player player;
vector<GameEngine::GameObject> gameObjects;

void MainComponent::init() {
    gameObjects.push_back(player);

    gameLoop();
}

void MainComponent::render() {
    for(int i = 0; i < gameObjects.size(); i++) {
        gameObjects[i].render();
    }
}

I would expect this code to output "Render Player", since the only object in the gameObjects vector is a Player, but instead it outputs "Render Game Object". Is there a way that I can force the Player object in the gameObjects vector to use the Player render as opposed to the GameObject render?

shadowarcher
  • 3,265
  • 5
  • 21
  • 33

4 Answers4

2

If a derived class is handled using pointer or reference to the base class, a call to a overridden virtual function would invoke the behavior defined in the derived class.

You must store pointer to the base class instead base object value into the vector.

Player player;
vector<GameEngine::GameObject*> gameObjects;

void MainComponent::init() {
    gameObjects.push_back(&player);

    gameLoop();
}

void MainComponent::render() {
    for(int i = 0; i < gameObjects.size(); i++) {
        gameObjects[i]->render();
    }
}
Alexcei Shmakov
  • 2,203
  • 3
  • 19
  • 34
1

Your vector contains GameObjects by value. Therefore pushing a subclass instance into this vector does object slicing: https://en.m.wikipedia.org/wiki/Object_slicing

You should be using GameObject pointers, or preferably shared_ptr<GameObject>

Tamás Zahola
  • 9,271
  • 4
  • 34
  • 46
1

Here's a simpler version of the code that illustrates the same problem:

GameEngine::GameObject object = player;
object.render();

The problem is that the player object gets converted to a GameObject by slicing off the base part of the player. So what gets stored is a GameObject, not a Player object.

The solution is to use a pointer or a reference instead of an object:

GameEngine::GameObject *ptr = &player;
ptr->render();

You need to do the same thing in gameObjects: instead of storing objects of type GameObject it should store pointers to GameObject:

std::vector<GameEngine::GameObject*> gameObjectPointers;
gameObjectPointers.push_back(&player);
gameObjectPointers[0]->render();

Once you do this you have to pay close attention to object lifetimes. If player gets destroyed before gameObjectPointers does, then gameObjectPointers[0] points to an object that doesn't exist any more, and using it will make bad things happen.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
1

Is there a way that I can force the Player object in the gameObjects vector to use the Player render as opposed to the GameObject render?

No. You're declaring the vector with type vector<GameEngine::GameObject> gameObjects;, so the base class' instances will be stored in the vector. And when gameObjects.push_back(player);, the object will be slicing copied, there's no Player.

You can store pointer of the base class into the vector, and better to use smart pointer to avoid manual memory mangement. For example:

vector<unique_ptr<GameEngine::GameObject>> gameObjects;

void MainComponent::init() {
    gameObjects.emplace_back(new Player);
    // or gameObjects.push_back(unique_ptr(new Player));
    gameLoop();
}

void MainComponent::render() {
    for(int i = 0; i < gameObjects.size(); i++) {
        gameObjects[i]->render();
    }
}
Community
  • 1
  • 1
songyuanyao
  • 169,198
  • 16
  • 310
  • 405