3

So I have a vector full of all the objects for my game; things like the player object, enemy object, walls, etc... All things in the vector are children of Framework, so I made the vector type Framework because that was the closest thing to a universal data type for them.

The problem was it wasn't running overridden functions from the objects it stored. So I Googled it to find out apparently I'm object slicing by storing them as Framework. So my question is then, how do store all these objects in one list?

Just for reference, this is where the supposed-to-be-overridden functions are called.

for (vector<Framework>::iterator num = gameObjects.begin(); num != gameObjects.end(); ++num)
{
    //The current thing
    Framework currentObject = *num;
    currentObject.frameEvent();
    currentObject.frameEndEvent();
    currentObject.drawEvent();
}

Thanks in advance.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
null
  • 548
  • 2
  • 6
  • 17

8 Answers8

7

You have to store pointers (Framework*), or std::shared_ptr< Framework > (possibly std::unique_ptr< Framework >) instances. That allows to use virtual calls and late binding - meaning that when you refer to your objects, the right function to call will be determined at runtime. Don't forget to make your functions virtual

Then, your code (in similar fashion)

for (vector< Framework* >::iterator num = gameObjects.begin(); num != gameObjects.end(); ++num)
{
  Framework* currentObject = *num;
  currentObject->frameEvent();
  currentObject->frameEndEvent();
  currentObject->DrawEvent();
}

However, I'd recommend sth like this (c++11 required)

vector< std::unique_ptr< Framework > > gameObjects;

...

for (auto & currentObject : gameObjects)
{
  currentObject->frameEvent();
  currentObject->frameEndEvent();
  currentObject->DrawEvent();
}

Last one (range for loop) should work regardless of the type used (you may use normal pointer or shared_ptr or unique_ptr as example shows).

If you are interested in using unique_ptr, please note that in order to store these on std::vector, you have to use std::move, as there can only be one instance of std::unique_ptr (as name suggests). Consult this answer in case of problems.

Community
  • 1
  • 1
lisu
  • 2,213
  • 14
  • 22
  • 1
    Thank you so much! It worked. But just wondering, what is the `->`? Is that how you call functions from pointers of classes? Again, thanks for the help! – null Aug 02 '15 at 22:12
  • 1
    Indeed, you use a (dot) `.` when you have an object and `->` when you have pointer (or pointer-like object - `std::shared_ptr` or `std::unique_ptr`). – lisu Aug 02 '15 at 22:23
2

You have to use std::vector<Framework*> or std::vector<std::unique_ptr<Framework>> (or smart pointer of your choice) to avoid object slicing.

Code example:

std::vector<std::unique_ptr<Framework>> gameObjects;

gameObjects.push_back(std::make_unique<Player>(/**/));
gameObjects.push_back(std::make_unique<Wall>(/**/));

And then

for (auto& currentObject : gameObjects)
{
    currentObject->frameEvent();
    currentObject->frameEndEvent();
    currentObject->drawEvent();
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
2

Store pointers to Framework and make use of polymorphism.

// Base class
class Framework {
    // Make pure virtual if nothing is done here.
    virtual void frameEvent(); // = 0;

    // To ensure the derived instances are deleted properly.
    virtual ~Framework(); // = default;
};

// Some specialized class
class SomeObject : public Framework {
    void frameEvent() override; // C++11 style override.
};

std::vector<std::unique_ptr<Framework>> objects;

// Add a new SomeObject to the list
objects.emplace_back(new SomeObject());
Beta Carotin
  • 1,659
  • 1
  • 10
  • 27
1

Well, the straight answer is - you don't. Or you'll get object slicing that you already encountered. You need to use the polymorphic abilities of C++ and store the common interface of your objects in the vector. Later you can invoke the desired behavior by calling the interface functions that have different implementation according to the actual object type.

SomeWittyUsername
  • 18,025
  • 3
  • 42
  • 85
1

You have to make frameEvent, frameEndEvent and drawEvent virtual in Framework. Then, store std::shared_ptr in your vector:

std::vector<std::shared_ptr<Framework>> objects;
objects.push_back(new GameObject());

for (vector<Framework>::iterator num = gameObjects.begin(); num != gameObjects.end(); ++num)
{
  //The current thing
  const Framework& currentObject = **num; // reference, don't copy anything if you don't need to
  currentObject.frameEvent();
  currentObject.frameEndEvent();
  currentObject.drawEvent();
}

Note that you can store a unique_ptr in a vector as long as you don't use operations that require copying elements.

Another option, if you can't make the functions virtual and know what type of object you are dealing with, you can cast first and then call the non-virtual functions:

const PlayerObject& player = static_cast<const PlayerObject&>(*objects[0]);
player.frameEvent();
player.frameEndEvent();
player.drawEvent();
Community
  • 1
  • 1
ChronoTrigger
  • 8,459
  • 1
  • 36
  • 57
  • Well, you can, but there are some limitations regarding copying elements. – ChronoTrigger Aug 02 '15 at 21:21
  • These limitations are actually helping you to know where the ownership of your (declared as unique!) objects get copied, which is very useful, especially for beginners. – wasylszujski Aug 02 '15 at 21:24
1

You need to store pointers to your objects. If you have C++11 then ideally you will store them using std::unique_ptr (assuming you have one master vector).

C++03

std::vector<Framework*> gameObjects;

for(std::vector<Framework*>::iterator num = gameObjects.begin();
    num != gameObjects.end(); ++num)
{
    (*num)->frameEvent();
    (*num)->frameEndEvent();
    (*num)->drawEvent();
}

C++ 11

std::vector<std::unique_ptr<Framework>> gameObjects;

for(auto& num: gameObjects)
{
    num->frameEvent();
    num->frameEndEvent();
    num->drawEvent();
}
Galik
  • 47,303
  • 4
  • 80
  • 117
0

Store pointers in your vector, instead of the actual instances.

zmbq
  • 38,013
  • 14
  • 101
  • 171
0

Another, possible faster approach is to store them in separate vectors. It can be 50 times faster due to higher cache efficiency.

user877329
  • 6,717
  • 8
  • 46
  • 88