My vector hell

Started by
12 comments, last by Will F 18 years, 10 months ago
Please help me sort out my vectors! I have a class called World, which manages my game world. World contains instances of my Maze and Actor classes, the Actors being the various characters and the Maze being the area where they move about. As you can see from the World header file, it contains a Maze called m_Maze, and a vector of Actors called v_Actor:

//world.h

class World
{
private:
	Maze m_Maze;
	std::vector<Actor> v_Actor;

etc
}


Let me know if this is correct so far, it certainly compiles fine. world.cpp has a funcion that is supposed to populate this world. I am able to create a new Actor easily enough: Actor horribleMonster; But of course I want lots of monsters, and I want to store them in v_Actor. How do I do this? I thought it was something like: v_Actor.push_back(new Actor); I would put this in a for next loop so that I can create millions of horribleMonsters and take over the world. But when I compile it I get: "no matching function for call to ` std::vector<Actor, std::allocator<Actor> >::push_back(Actor*&)' " (using DevCPP) What does it all mean?
Advertisement
The type returned by new Actor() is a pointer to memory representing an actor i.e. of type Actor*. The vector is std::vector<Actor> v_Actor; and contains instances of actors i.e. of type Actor. Hence there is no push_back function that applies to this case.

So either you have to leave the vector as is and create new actors by:
Actor a;vActors.push_back( a );

Or you can redefine the vector by:
std::vector<Actor*> v_Actor;vActors.push_back( new Actor() );


It is up to you, although I favor something along the lines of the latter approach. Greetz,

Illco
Would the first approach only work if I wanted to create a small and fixed amount of Actors? eg:

Actor bigMonster;
Actor biggerMonster;
Actor biggestHairiestMonster;
vActors.push_back( bigMonster );
vActors.push_back( biggerMonster );
vActors.push_back( biggestHairiestMonster );

I want to create lots, maybe even a random amount. Is the second approach the best for that?

Or is my .h file wrong in some way?
If you're going to be creating lots then dynamic allocation using std::vector<Actor*> would probably be best... but, you do need to be careful about remembering to delete the elements in your std::vector when you're done with it. Good source of memory leaks I find [smile]

hth
Jack

<hr align="left" width="25%" />
Jack Hoxley <small>[</small><small> Forum FAQ | Revised FAQ | MVP Profile | Developer Journal ]</small>

So that goes in my header file?

std::vector<Actor*> v_Actors;

Would that mean that my vector holds pointers to Actors?

Or have I got the wrong end of the stick?

Feel a bit lost really...
Quote:Original post by darenking
So that goes in my header file?

Yup, looks like you could just add a "*" into the line you've already got [smile]

Quote:Original post by darenking
std::vector<Actor*> v_Actors;

Would that mean that my vector holds pointers to Actors?

Yes.

You should use it similar to the following:
Actor* myActor = new Actor();
v_Actors.push_back( myActor );

I think this will also compile, but I wouldn't use it [smile]:
Actor anotherActor;
v_Actors.push_back( &anotherActor );

Quote:Original post by darenking
Feel a bit lost really...

the STL containers can be a real pain to get your head around at times - I've pretty much got to the point where I know how to use them to do what I want to do, but I would never say that I fully understood all of the finer details [smile]

The thing with std::vector's containing pointers is you have to be a bit careful when dereferencing iterators (e.g. looking through the vector) and you should also make sure that you (as I previously said) iterate through the vector and delete each element before you finally ditch the actual vector object. Typically something you'd want to stick in your class destructor.

hth
Jack

<hr align="left" width="25%" />
Jack Hoxley <small>[</small><small> Forum FAQ | Revised FAQ | MVP Profile | Developer Journal ]</small>

OK, I've now added a * in my world.h file, to create a vector of pointers to Actors:

	std::vector<Actor*> v_pActors;


Easy! And I've created a loop in world.cpp to create five actors.

	for ( int actor=0 ; actor<5 ; actor++ )	{		Actor* tempActor = new Actor();		v_pActors.push_back( tempActor );	}


Am I right in thinking that tempActor doesn't exist as an Actor, it's just a pointer to an Actor? So we're not creating an Actor that isn't needed?

Do I now have five actors, all initiated with the default constructor (therefore all identical)? How can I check? Do I use the sizeof thingy?
Quote:Original post by darenking
Am I right in thinking that tempActor doesn't exist as an Actor, it's just a pointer to an Actor? So we're not creating an Actor that isn't needed?

Correct.

Quote:Original post by darenking
Do I now have five actors, all initiated with the default constructor (therefore all identical)? How can I check? Do I use the sizeof thingy?

You need to use the vector::size() method, which according to the above fragment should be: ( v_Actor.size() == 5 ).

hth
Jack

<hr align="left" width="25%" />
Jack Hoxley <small>[</small><small> Forum FAQ | Revised FAQ | MVP Profile | Developer Journal ]</small>

I think has been explained satisfactorily, but I'll go over it again just in case. Your vector is declared to hold instances of Actor, but the new operator returns a pointer to an Actor. A pointer to an Actor is not an Actor, so your compiler can't figure out how to put it into a vector of Actors. The solution is either to actually put Actor objects into your vector:
void generateMonsters(){	for (unsigned int monsterNumber = 0; monsterNumber < numberOfMonsters; ++monsterNumber)	{		actors.push_back(Actor());	}}

or to declare your vector to hold pointers and push pointers onto it:
class World{	private:		Maze maze;		std::vector< Actor * > actors;};void generateMonsters(){	for (unsigned int monsterNumber = 0; monsterNumber < numberOfMonsters; ++monsterNumber)	{		actors.push_back(new Actor());	}}

The second version requires you to take responsibility for deleteing each Actor from the vector when you are done with it. A safer approach would be to use a smart pointer:
class World{	private:		Maze maze;		std::vector< boost::shared_ptr< Actor > > actors;};void generateMonsters(){	for (unsigned int monsterNumber = 0; monsterNumber < numberOfMonsters; ++monsterNumber)	{		actors.push_back( boost::shared_ptr< Actor >(new Actor()));	}}

The non-pointer version may seem to be easier, but it will only allow you to store actual Actors. If you later define further classes which inherit from Actor you will only be able to store the Actor parts of them in the vector:
class Actor{	public:		virtual void whatAmI() const		{			std::cout << "I am an Actor\n";		}};class Monster	:	public Actor{	public:		virtual void whatAmI() const		{			std::cout << "I am a Monster\n";		}};class World{	public:		std::vector< Actor > actors;		std::vector< boost::shared_ptr< Actor > > actorPointers;};int main(){	World world;	world.actors.push_back(Actor());	world.actors.push_back(Monster());	world.actorPointers.push_back(boost::shared_ptr< Actor >(new Actor()));	world.actorPointers.push_back(boost::shared_ptr< Actor >(new Monster()));	world.actors[0].whatAmI();	world.actors[1].whatAmI();	world.actorPointers[0]->whatAmI();	world.actorPointers[1]->whatAmI();}

Output:
I am an ActorI am an ActorI am an ActorI am a Monster

For this example you'll need to get hold of the boost smart pointer library.

Quote:Original post by jollyjeffers:
I think this will also compile, but I wouldn't use it :
Actor anotherActor;
v_Actors.push_back( &anotherActor );

You could do it, but it would be a really bad idea. As soon as anotherActor leaves scope it ceases to exist and the pointer in v_Actors becomes a dangling pointer. You no longer have a pointer to an Actor. You have a pointer to random memory.

Finally, since you seem to be using a type of hungarian notation I'll recommend this article to you.

Enigma
Thank you for that amazingly detailed answer. I think I'll do the middle one for now, a vector of pointers to Actors, get that working, then try the smart pointers at a later date.

I hope you all like my new avatar.

This topic is closed to new replies.

Advertisement