Reputation: 1429
I have a std::vector<Enemy*> enemies
and when i create a new Enemy
i do enemies.push_back(this)
. This works for what i'm doing, but i'm wondering if there is a way to have the vector not need pointers to the enemies. I've tried just std::vector<Enemy>
but it seems to cause a bunch of problems i can't figure out.
ie. - had to change to enemies.push_back(*this)
, and it caused some linker errors for some reason.
looping through with the following works, but removing the pointers creates issues:
void Enemy::updateEnemies(sf::RenderWindow &window) {
for(std::vector<Enemy*>::iterator it = enemies.begin(); it < enemies.end(); it++) {
(*it)->update(window);
}
}
Wondering if someone could point me in the direction of using a vector without pointers and loop through it, updating the objects inside it with the same function..
Upvotes: 3
Views: 1467
Reputation: 311
If you want a well-founded answer you have to go one step back - to the design. As far as i see the Enemy objects are registering themselves via constructor in a kind of global list. So far so good.
But how these object are produced ? In this example in Enemy can be construct everywhere
Depending on this, the Enemy-object cant do any reliable statements about its storage. But one thing that all objects share and what is always accessible from inside the object - is the address ('this'). So the solution via pointer is the only solution that is universal for all constructed Enemy-objects. (References are in this context also "a kind of pointer", and other more complicate constructs may also rely on pointers).
A std::vector cant work because you get 2 different objects. To change one of them does not effect the other. That wont work. And it is not possible to switch to smart pointers without changing your design.
There are 2 common solutions:
Managing the listing from 'inside' Enemy in a generally way like in the given example. Expandable with more features like auto delete entries via destructor.....
Managing the listing from 'outside'. Enemy-objects dont register themselves, the generator of the Enemy-objects is responsible for to do it proper (the design pattern 'factory' is a good solution in this case). And you can use smart pointers what is a good idea in most cases.
There are also some more solutions (including some good solutions) unmentioned, they exceed the scope of the question.
Upvotes: 1
Reputation: 15349
My practice is to make sure the class implements (and makes public
) all of the following: a default constructor, a copy constructor, an assignment operator, and a destructor. Then there's no problem with things like std::vector<Enemy>
. (The default constructor isn't even necessary for vectors, but can be handy for various other things.)
One caveat, though: when you push_back()
an Enemy
into your vector, it's actually a copy of the original instance that gets stored. This raises a few gotchas: first, changes to the original (e.g. to the member data of the variable e
in the main()
scope of my example below) will not affect the copy stored in the vector. If you're accustomed to working with vectors-of-pointers, this may be surprising. Second, the implicit construction of copies and the copying of member data will take time, which may or may not have a significant effect on performance. Third, implicit construction and copying may trigger side-effects (you should simply avoid these in your design, wherever possible).
Here's an example:
#include <vector>
#include <iostream>
class Enemy
{
public:
Enemy() : mEvilness( 1 ) {} // default (i.e. parameterless) constructor
Enemy( const Enemy& other ) { Copy( other ); } // copy constructor
Enemy& operator=( const Enemy& other ) { return Copy( other ); } // assignment operator
~Enemy() {} // destructor
Enemy( int evilness ) : mEvilness( evilness ) {} // standard constructor
void Gloat( void ) const
{
std::cout << "muahaha";
for( int i = 0; i < mEvilness; i++ ) std::cout << "ha";
std::cout << "!\n";
}
int mEvilness;
private:
// common helper method for copy constructor and assignment operator:
Enemy& Copy( const Enemy& other )
{
mEvilness = other.mEvilness; // make copies of any other member data here
return *this;
}
};
typedef std::vector< Enemy > EnemyHorde;
void AllGloat1( const EnemyHorde& h ) // doesn't matter if you pass by reference...
{
for( EnemyHorde::const_iterator it = h.begin(); it != h.end(); it++ )
it->Gloat();
}
void AllGloat2( EnemyHorde h ) // ...or value (but this will take more CPU time)
{
for( EnemyHorde::const_iterator it = h.begin(); it != h.end(); it++ )
it->Gloat();
}
int main( int argc, const char* argv[] )
{
EnemyHorde h;
Enemy e( 1 );
h.push_back( e );
h.push_back( Enemy( 2 ) ); // even transient Enemy instances get copied and stored
h.push_back( Enemy( 3 ) );
AllGloat1( h );
std::cout << "\n";
e.mEvilness++; // this will have no effect on the output because h contains a *copy* of e
AllGloat2( h );
return 0;
}
Upvotes: 1
Reputation: 50190
everbody is saying it in comments - but its the answer
use smart pointers
if you dont know how to do that then google
You life is much easier, safer, faster,...
And they are really easy to use - the main trick with using them is to trust them and dont try to get clever or 'help' them
Upvotes: 2
Reputation: 57708
You don't need pointers (most of the time) in C++.
A copy will be made when using the std::vector::push_back()
method.
Example:
Enemy evil_wizard;
std::vector<Enemy> enemy_container;
enemy_container.push_back(evil_wizard);
Enemy Evil_knight;
enemy_container.push_back(Evil_knight);
Enemy pink_elephant;
enemy_container.push_back(pink_elephant);
Upvotes: 0