Search code examples
c++pointersvectorbase-class

Is there a way to call method with base class pointer that uses the data in derived class?


(sorry for my bad english)

I have a base class with vector of pointers on Drawable objects in it and method draw() that uses data from this vector.

class GameObject
{
protected:
    std::vector<Drawable*> drawable;
...

void GameObject::draw() { for (const auto& object : drawable) window.draw(*object); }

In the derived classes I want to have an ability to add some Drawable objects

class Player : public GameObject
{
protected:
    RectangleShape shape;
...

Player::Player(float x, float y, float z)
{
    shape.setPosition [...]
    drawable.push_back(&shape);
...

and draw them using method of base class pointer

std::vector<GameObject*> gameObjects;
...
for (auto& gameObject : gameObjects) gameObject->draw();

The program crashes (I think because the base class don't know anything about vector data in derived class). I understand that I could make this method pure virtual and define it in the derived classes, but it's not that convenient. Maybe there is another way more similar to this?

upd:

Level::Level()
{
    player = Player(500.f, 500.f); //Player player; in header file
    gameObjects.push_back(&player);
}

void Level::display()
{
    for (auto gameObject : gameObjects) gameObject->draw();
}

Solution

  • The problem is in the code added by your edit -- it looks like my crystal ball is working today.

    You're creating a temporary Player and moving it into the player member variable. That ends up with a vector holding the address of the shape inside the temporary Player, which is immediately destroyed, leaving a dangling pointer.

    Use a ctor-initializer-list to avoid the move:

    Level::Level()
        : player(500.f, 500.f /* where did Z go? */)
    {
        gameObjects.push_back(&player);
    }
    

    And disable the assignment operators to prevent doing this by accident in other places:

    class Player
    {
        // ...
    
        Player& operator=(const Player&) = delete;
        Player& operator=(Player&&) = delete;
    };