• Advertisement

Archived

This topic is now archived and is closed to further replies.

pointers sharing dymanic memory

This topic is 4970 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

high, im working on a 2d RPG w/ c++ and OpenGL. i have an updating "engine", which will update each of my objects. i have a class called World_Update, which contains a virtual bool Update(). most of my classes inherit from World_Update. then, World_Update has a static member which is list''s of World_Update*. my world_update class also has a static member Register(World_Update*); which will register an object into the updating engine. heres the problem : I have a global list of Enemy* called list < Enemy* > world_enemies. this is a list of any enemies that are in the world. my player class has a member which is a vector < Enemy*>. this is basically just a list of any enemies that are on the screen that he can target. when the player goes to target, i will loop through EVERY enemy in the world_enemies list, call On_Screen(), and if the enemy is on the screen, he will be placed in the vector of Enemy*. ok, so now all i have to do is register any enemies i make with the updating engine, AND the world list of enemies. i do this while reading in a map: if( there is an enemy here) { Enemy *e = new Enemy(); World_Update::Register(e); world_enemies.push_back(e); } OK - now is where i should tell you about the updating engine. you see,i do something like this : (for each object in my world update list) { if( ! itor->Update()) { delete *itor; itor = world_update.erase(itor); } } basically, an object is removed from the world when his Update() returns false. this could be when an enemy dies, when a particle''s life ends, whatever. the problem is - what happends when an enemy dies? he is removed from the world_update list, BUT his address is still sitting in the world_enemy list!!! this pointer is now garbage but its kept in my world_enemy list and i have no way of telling if its in-valid. im just looking for suggestions on how to handle this. if you suggest auto ptrs or a boost one, could you please give an example on how they are used? thanks for any help!!!

Share this post


Link to post
Share on other sites
Advertisement
yes, either use a smart pointer or implement a message broadcast system. when any unit dies it sends a broadcast message that goes to basically every object in the game that's been registered as a listener to the broadcast system. when the player gets this message he will "care" about it and just remove the pointer from his list.

the alternative is to redesign your engine so that people don't hold pointers but rather they hold objectIDs (int) of the enemy objects. you hold all objects in one global list and implement a method of storing them, like a hashmap, that is quickly acessable. it will introduce a little bit of overhead and latency but it will save you lots of debugging headaches.

-me

[edited by - Palidine on June 9, 2004 9:45:50 PM]

Share this post


Link to post
Share on other sites
some ideas:

- Don''t store a list of enemies to track.

- Flag any update object with some kind of "don''t kill me" message so the engine doesn''t delete him, just erase.

- switch to using handles or ID as Palidine suggest. Give each object an ID stamp and then when you want a pointer to that enemy, you call

Enemy* SomeGlobalReserve::GetEnemyByID( int ID );

which returns NULL if there is no enemy with that ID (he''s dead, Jim). I''ve used that trick before. THe problem is that you have to reacquire the pointer everytime you want to use it which is bad for an action game :-( but great for a one-time lookup :-)

- and of course the auto_ptr, but i''ve never actually used one before. It would be good if you are putting pointers to dynamic memory in many object''s hands.

Share this post


Link to post
Share on other sites
quote:
Original post by leiavoia
some ideas:

- Don''t store a list of enemies to track.



then how do i retrive the enemies in the world, and find out if they are on screen for the player''s targetting?

quote:

- Flag any update object with some kind of "don''t kill me" message so the engine doesn''t delete him, just erase.



yes, but then i have a bunch of pointers laying around in space? if not, who deletes it?

quote:

- switch to using handles or ID as Palidine suggest. Give each object an ID stamp and then when you want a pointer to that enemy, you call

Enemy* SomeGlobalReserve::GetEnemyByID( int ID );

which returns NULL if there is no enemy with that ID (he''s dead, Jim). I''ve used that trick before. THe problem is that you have to reacquire the pointer everytime you want to use it which is bad for an action game :-( but great for a one-time lookup :-)



im trying to envision this sytem in my head... gotta think about it for a minute

quote:

- and of course the auto_ptr, but i''ve never actually used one before. It would be good if you are putting pointers to dynamic memory in many object''s hands.


i think ill just wind up looking into a boost::shared_ptr then...

thanks for anymore suggestions!!!

Share this post


Link to post
Share on other sites
Memory management is a tricky subject. I have Game Programming Gems and it discusses a couple different approachs to this. Handles and auto-pointers are good ideas. Maybe you could revise you World list so it maintains several lists; such as an EnemyList, PowerUpList and so on.
--L2S

Share this post


Link to post
Share on other sites
quote:
yes, but then i have a bunch of pointers laying around in space? if not, who deletes it?


Somebody else, obviously :-) You can have lots of pointers out there, just don''t have lots of people trying to delete them. Of course, even that''s not very safe, but you''re pretty much the only person working on it i''m sure, so i wouldn''t worry TOO much about that.

You might try accessing specific class objects (Enemy) via a class''s insternal list of objects.

Share this post


Link to post
Share on other sites
hmm... so maybe ill make a static::list of Enemy*, then in the enemy constructor i would push back ''this'' into the list, and in the destructor i would erase it from the list (but not delete it of course).... thanks for the help

Share this post


Link to post
Share on other sites
ok, im really new to copy constructors and ive never had to find something to erase it.. so i just want to make sure im implementing this right. first, the Enemy class gets a member

static list < Enemy* > enemies_in_world;

now, inside of the constructor:


Enemy::Enemy()
{
enemies_in_world.push_back(this);
}


now inside the destructor:


Enemy::~Enemy()
{
enemies_in_world.erase(find(enemies_in_world.begin(),enemies_in_world.end(),this);
}


(will this work? or do i have to overload the == operator for find to work properly? the STL docs werent very clear)

now, since my enemies are always in list's (ie being copied a lot), im not sure how to implement the part with the copy constructor / assignment operator. this is what i got so far:

in the copy constructor:


Enemy::Enemy(const Enemy &cpy)
{
enemies_in_world.push_back(this);
}


and the assignment operator ?


Enemy Enemy::operator = (Enemy cpy)
{

Enemy temp;
enemies_in_world.push_back(&temp);

return temp;
}


so am i doing this right? like i said im very new to op overloading, copy constructors, and using the find() function.. please tell me if i did this right.. thanks again!!

[edited by - graveyard filla on June 10, 2004 4:35:34 AM]

Share this post


Link to post
Share on other sites
Your assigment operator is not a proper assigment operator it should be something like this:


Enemy& Enemy::operator=(const Enemy& cpy) {
return *this;
}


And you have to define an equality operator for find or use a functional object.

this is a member function equality operator:

bool operator==(const Enemy& e) const {
return this->val == e.val;
}


or a non member function version

bool operator==(const Enemy& e1, const Enemy& e2) {
return e1.val == e2.val;
}

Share this post


Link to post
Share on other sites
Hello graveyard filla,

I would have a master map which has as it key the pointer to World_Update base class.
It would then have a class that would have a list of all list that World_Update object is in.

ex.
you create an enemy he gets add to world list and also the world_enemies list.

on each add you go the master world object map use the pointer of the enemy that is geting added.
Then in the list_class return from the map, add the object that is added the enemy.
once for world_update list and world_enemy list.
If this enemy is added to your player he does the same.

If enemy dies you go to the master world object map use it pointer to get it list_class go through each list and remove form each then
erase this object entry and finish delete object.

Then all you would need is each class store in the list_class has the same base class with same add/remove World_Update form internal list.

Do you get my point?

Lord Bart

[edited by - lord bart on June 10, 2004 10:53:56 AM]

[edited by - lord bart on June 10, 2004 11:04:11 AM]

Share this post


Link to post
Share on other sites
quote:
Original post by snk_kid
Your assigment operator is not a proper assigment operator it should be something like this:


Enemy& Enemy::operator=(const Enemy& cpy) {
return *this;
}


And you have to define an equality operator for find or use a functional object.

this is a member function equality operator:

bool operator==(const Enemy& e) const {
return this->val == e.val;
}


or a non member function version

bool operator==(const Enemy& e1, const Enemy& e2) {
return e1.val == e2.val;
}



snk kid, thanks for your help. could you explain things a little more? first off, i dont understand the = operator. lets say i do this:

enemy1 = enemy2;

this will call enemy1's assignment operator, sending enemy2 as the arguement? now it hops into enemy1:: = operator, and it just returns the value stored at 'this'. but 'this' is enemy1, who hasnt been constructed yet and therefore all his values are empty.. im just a little confused on what to do, how this all flows, and do i only return *this or do i copy values also?

also, how would i go about overloading the == operator so that find will remove 'this' pointer from the list? keep in mind i have a list < Enemy* >... in the constructor for Enemy i do enemy_list.push_back(this)... so im not positive, but i think i need to overload the == operator so that 'find' will find 'this' and erase it... but how would that look exactly? would this be right?

bool operator ==(const Enemy& e1, const Enemy& e2)
{
return &e1 == &e2;
}

so is this right? also, the global version of the == operator sounds more strait - up then the classes version, but thats only because i dont understand how the class version works... which one is sent in as the paremeter? im thinking the one on the right side of the == sign? thanks for anymore help!!

ps- bart, didnt really get it, ill have to read your post a few more times

[edited by - graveyard filla on June 10, 2004 10:39:23 PM]

[edited by - graveyard filla on June 10, 2004 10:41:37 PM]

Share this post


Link to post
Share on other sites
but 'this' is enemy1, who hasnt been constructed yet and therefore all his values are empty

Do not confuse copy constructors and assignment operators. While their semantics are similar (in fact, it would be wrong for an assigment operator to behave too differently from the associated copy constructor), the fact is, in an assignment operator, both objects already exist, while the copy constructor does actually create a new object. Typically:

struct Foo
{
Data* data;

Foo()
: data(new Data) // Acquire resource

{};

~Foo()
{
delete data; // Release resource

}


Foo(const Foo& rhs) // Copy constructor

: data(new Data(*rhs.data) // Acquire resource and copy information

{}

Foo& operator=(const Foo& rhs) // Assignment operator

{
if( this == &rhs ) // Guard against self-assignment

return *this;

delete data; // Release old resource

data = new Data(*rhs.data); // Acquire resource and copy information


// Note that depending on the nature of the resource & data

// you could have copied the data your were holding directly

// into the old memory, without the release old/allocate new

// dance. A typical counter example is for string classes or

// any other case where you may need to allocate a different

// amount of memory (e.g. to accomodate a longer string)


// The self-assignment guard is necessary, not only for

// performance reasons (self-assignment is a no-op), but

// principally for correctness, since we are deleting the

// old object prior to the copying. Without it, we would

// be deleting the very object we are trying to copy.


// "return *this" enables chained assignment (a = b = c)

return *this;
}
};


Note how the code that does the actual copying is the same in the copy constructor and the assignment operator - even though the code for the copy constructor is, as is proper, placed in the initialization list. The main difference lies in the fact that the assignment operator must take care of cleaning up the old object prior to overwriting it (proper class construction may reduce the amount of code needed to do that).

.. im just a little confused on what to do, how this all flows, and do i only return *this or do i copy values also?

You need to actually copy the values in the object. "return *this;" is only there to support chained assignments.

also, how would i go about overloading the == operator so that find will remove 'this' pointer from the list?

Your comparison operations MUST NOT have side effects (hint - look at the const keyword). Having side effects there would violate many assumptions both in the standard library, and in what programmers expect - when programming, it is good to respect the "Principle of Least Surprise" (i.e. your code should do what it looks like it does).

Additionally, there is no (non-kludgy) way you can let your operator== know what container it is supposed to remove the object from. And since == ought to be commutative, which of the two objects being compared would you remove from which container?

so im not positive, but i think i need to overload the == operator so that 'find' will find 'this' and erase it

It's entirely up to you to define what == means ... so long as it does test for equivalence

bool operator ==(const Enemy& e1, const Enemy& e2)
{
return &e1 == &e2;
}


Here, you are testing for identity , which is a much stronger form of equivalence. Two strings containing the same text would be equivalent, but you would only have identity if they were really one and the same object. Since you have a container of pointers, overloading Enemy equivalence tests to be identity test is quite pointless - you can direcly work on the pointers instead.

also, the global version of the == operator sounds more strait - up then the classes version, but thats only because i dont understand how the class version works...

When binary operators are implemented as member functions, *this takes the place of the left (first) parameter of the non-member equivalent implementation. So basically foo == bar; would either be foo.operator==(bar); (member) or operator==(foo, bar); (non-member).

This has an important consequence when the two parameters aren't of the same type. Since the member function implementation automatically has its left-hand side argument be the *this object, you cannot always implement commutative operations in that way. If both arguments are user-defined classes, you would put the code for Foo + Bar in the Foo class and the code for Bar + Foo in the Bar class. But in the case of basic types this is not possible, as they don't have user-definable member functions. So int + Foo cannot be a member function.
It is therefore good pratice to implement 'mixed-mode' binary operators as non-member (possibly friend) functions. And, in my opinion, it's just as good to implement them all as non-members, for regularity's sake.



“Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.”
— Brian W. Kernighan


[edited by - Fruny on June 10, 2004 12:03:49 AM]

Share this post


Link to post
Share on other sites
hey fruny,

whoa, sorry about all the questions, thanks for any help..

when (if ever) is = called without you knowing? also, is it true that the deststructor will always be called, but not always a contructor? sometimes its a copy constructor (like when using containers) or = operator even?

also, so technically, a = operator doesnt even have to return anything, it does the copying directly in the function? it only returns *this for chaining? and how does that work exactly?

also, in the == operator, when you do

return &e1 == &e2

since im calling this with a std::list of pointers via the find() function, does this (&e1 or &e3) say "the address of the pointer", or "the address of the object its pointing to ie the pointer itself" i think you meant the former but i just
want to make sure. basically, im a little confused on how this works, i mean, when i have a pointer, and call new, ie:

Enemy e = new Enemy();
Enemy e2 = new Enemy();

if(e == e2)

ok, your right i guess it would send the pointer and doing & would be a pointer to a pointer, so if i wanted to keep the ampersands i would have to de-reference when i sent them?

also, i never worked with initialization lists before except when calling a parents constructor from inside of a child class''s constructor

so you can do this with any function? but really your example does the same thing if you would replace the : with { and then put a semicolon and another bracket
at the end, correct? (ie you just put that code in the body of the constructor)

another thing that confused me a little was doing
new Data(*rhs.data)...

this is probably a stupid question, but did you purposely not use the = sign or is that just the way you like writing it?

lastly, in the = operator, you delete the data then make a new one. why? since the object already exist''s, shouldnt his constructor allocate the memory for him? and yes, i would like unique pointers / objects inside each Enemy.

thanks a lot for all your help!!

Share this post


Link to post
Share on other sites
when (if ever) is = called without you knowing?

Function parameters that are passed by value are copy-constructed. Idem for return values (though that copy-constructor call may be eliminated by the compiler).


also, is it true that the deststructor will always be called

Unless you mess up and write erroneous code (e.g. deleting a derived object via a pointer to base without having made your destructor virtual).

but not always a contructor?

Constructors, if any, are always called when you create an object.

sometimes its a copy constructor (like when using containers) or = operator even?

Foo a;     // Default constructor
Foo b(a); // Copy constructor
Foo c = b; // Copy constructor
a = c; // Assignment operator

also, so technically, a = operator doesnt even have to return anything

It doesn''t have to, but it''s idiomatic to return a reference to the object (i.e. return *this;)

it does the copying directly in the function?

Yes, you do have to write all the copy code yourself.

it only returns *this for chaining? and how does that work exactly?

Precisely. a = b = c; is functionally equivalent to a.operator=(b.operator=(c));. By having b.operator=(c); return a reference to b, the code becomes equivalent to b.operator=(c); a.operator=(b). If your assignment operator doesn''t return anything (void), that code is illegal, and you''ll be forced to write b = c; a = b;. Even though you may not find chained assigmnent very useful, people will expect it to work - it works in C, it works for the basic types, why would your class be any different?

does this (&e1 or &e3) say "the address of the pointer", or "the address of the object its pointing to ie the pointer itself"

The function is declared as bool operator==(const Enemy& e1, const Enemy& e2) so &e1 and &e2 are the addresses of the objects. That is the pointers themselves.

Enemy e = new Enemy();
Enemy e2 = new Enemy();

if(e == e2)


You''re comparing the pointers, not the objects, which means that your operator== is not even called.

so if i wanted to keep the ampersands i would have to de-reference when i sent them?

I haven''t read the thread in full - are you looking for a specific object to remove from the container (i.e. knowing its address), in which case you would just do a find based on the value of the pointer, no operator= needed, based on equivalence (i.e. all objects equal to a given one - like two strings with the same text) or are you trying to remove objects from the container based on their properties (i.e. all the ''dead'' objects), in which case find_if with an appropriate predicate would work?

also, i never worked with initialization lists before except when calling a parents constructor from inside of a child class''s constructor

Putting member initialization in the initializer list makes it so the members are directly constructed with the proper value, instead of first being default-constructed and then assigned a new value - which is obviously slower. Plus, if a member is either a constant or a reference (or generally, something that is not default constructible or not assignable to), you have no choice but put it in the initializer list.

It is the same difference as between Foo b; b = a; and Foo b(a); (or Foo b = a;.

so you can do this with any function?

No. Constructors only.

but really your example does the same thing if you would replace the : with { and then put a semicolon and another bracket
at the end, correct? (ie you just put that code in the body of the constructor)


No, not only the syntax isn''t right (you would need to turn it into an assignment), the semantics are different (see above).

another thing that confused me a little was doing
new Data(*rhs.data)...

this is probably a stupid question, but did you purposely not use the = sign or is that just the way you like writing it?


It is standard syntax when the constructor for the object you are dynamically creating takes parameters.

lastly, in the = operator, you delete the data then make a new one. why?

Read the comments.

since the object already exist''s, shouldnt his constructor allocate the memory for him?

The constructor did allocate memory when the object was created, but as I said in the comments, I am handling the general case where you don''t know if the amount of memory that had been allocated originally is sufficient to hold the new data. If Data* were a char*, the original object held enough memory for 6 characters (e.g. it held the string "Hello"), and you want to copy 12 characters in ("Hello world!"), you would need to reallocate memory.

If you are 100% sure that the size of the object never changes, then yes, you can do without the destruction/construction and do a direct assignment (*data = *rhs.data). But then, of course, you''ll have to make sure you don''t need an assignment operator for the Data class.


“Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.”
— Brian W. Kernighan

Share this post


Link to post
Share on other sites
hey fruny, thanks a lot , that helped a lot.. but more questions (i promise theres not as many this time )



I haven''t read the thread in full - are you looking for a specific object to remove from the container (i.e. knowing its address), in which case you would just do a find based on the value of the pointer, no operator= needed, based on equivalence (i.e. all objects equal to a given one - like two strings with the same text) or are you trying to remove objects from the container based on their properties (i.e. all the ''dead'' objects), in which case find_if with an appropriate predicate would work?


im trying to remove objects based on .. umm.. themselves. like you said, the value of the pointer. so how then? basically it goes like this:

i have an Enemy class. one member is a static list < Enemy* > enemy_list;

now, inside of the constructor of Enemy, i do this:
enemy_list.push_back(this);

so any enemy that is in the world, his address is in the list of enemies in the world... of course i do the same in the copy constructor and (trying to put) in the = operator...

in the destructor to the Enemy class, i want to remove the enemy from the list, so that any enemies in the list definetly exist (i create all my enemies dynamically). so far, my Enemy destructor looks like this:

Enemy::~Enemy()
{
enemy_list.erase(find(enemy_list.begin(),enemy_list.end(),this));
}

you see, in the constructor i add the enemy to the list, and in the destructor i (try to) remove it. so any enemies currently in the world will be in a nice list that i can retrieve to do my bidding...

so i dont even have to mess with the == operator? everythings good how it is, or do i have to overload == and in the body do the &e1 == &e2 like i said in my other post?

quote:

Putting member initialization in the initializer list makes it so the members are directly constructed with the proper value, instead of first being default-constructed and then assigned a new value - which is obviously slower. Plus, if a member is either a constant or a reference (or generally, something that is not default constructible or not assignable to), you have no choice but put it in the initializer list.



what do you mean, "instead of being default-constructoed and then assigned a new value".. i mean, if my constructor looks like this:

data = new Data();

how is this being default constructed and then assigned a new value? where is it default constructed? im just directly and immediately assigning a value and not touching it again...


No. Constructors only
i meant "so you can call any function from your initialization list?"



It is standard syntax when the constructor for the object you are dynamically creating takes parameters.


why is that? i have a bunch of classes who''s constructors take no parameters - and i dont think i make any of my objects on the stack. is this bad or something? why do i need parameters if it doesnt make sence for the class?



If you are 100% sure that the size of the object never changes, then yes, you can do without the destruction/construction and do a direct assignment (*data = *rhs.data). But then, of course, you''ll have to make sure you don''t need an assignment operator for the Data class.


well, the memory i allocate is of Type Weapon - Weapon is a parent of SMG, pistol, flamethrower - so whenever i need a different weapon, i just do current_weapon = new Flamethrower(), or current_weapon = new SMG(), i remember snk_kid told me there was a way to check what data type it was before i allocated (i mean, how am i supposed to knowwhich weapon the original copy holder had?) but is this a sloppy thing to do, or what?

thanks again for all your help! ugh, maybe there was just as many questions

Share this post


Link to post
Share on other sites
im trying to remove objects based on .. umm.. themselves. like you said, the value of the pointer. so how then?

The simplest way:
Enemy* enemy_to_remove = whatever;
enemy_list.remove(enemy_to_remove);


Rather inefficient if you have a number of enemies to remove (I would do a first pass to mark the enemies to remove - with a 'remove-me' flag, and then a second pass with remove_if to remove them all in one go, but that would require modifying your Enemy class).

so any enemy that is in the world, his address is in the list of enemies in the world... of course i do the same in the copy constructor and (trying to put) in the = operator...

I see. The assigment operator doesn't need to modify the world list at all, because there still are two separate objects. They just happen to contain equivalent data.

so far, my Enemy destructor looks like this

Use list::remove instead of find and list::erase.

so i dont even have to mess with the == operator? everythings good how it is, or do i have to overload == and in the body do the &e1 == &e2 like i said in my other post?

You're working with object identity, not object equivalence, so you're fine without overloading operator==.

if my constructor looks like this: data = new Data();

how is this being default constructed and then assigned a new value? where is it default constructed? im just directly and immediately assigning a value and not touching it again...


Members that are not listed in the initializer list are all default constructed when the object is created. Therefore, when the Foo object is created, data originally receives a NULL value. Only once all the initalizations are done will the constructor's code block be executed and your "data = new Data()" assignment be done.

i meant "so you can call any function from your initialization list?"

Not really. All you can do is pass parameters to the base class constructors and to the member variable constructors. If those parameters are results from other functions, that's ok.


why is that? i have a bunch of classes who's constructors take no parameters - and i dont think i make any of my objects on the stack. is this bad or something? why do i need parameters if it doesnt make sence for the class?

Well, the copy constructor does take a parameter. My copy constructor for Foo does rely on the copy constructor for Data. Again, why would I create a default Data object (with new Data()) only to immediately overwrite its contents with those from*rhs.Data?

well, the memory i allocate is of Type Weapon - Weapon is a parent of SMG, pistol, flamethrower - so whenever i need a different weapon, i just do current_weapon = new Flamethrower(), or current_weapon = new SMG()

Then you must destroy the old object. The new object isn't even guaranteed to have the same type. The object's vtable also needs to be properly initialized (which is done by the constructor). Using direct assignment here will be catastrophic.

i remember snk_kid told me there was a way to check what data type it was before i allocated (i mean, how am i supposed to knowwhich weapon the original copy holder had?) but is this a sloppy thing to do, or what?

Manually checking the types isn't justified here. Just delete the old weapon and create a new one.




“Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.”
— Brian W. Kernighan


[edited by - Fruny on June 11, 2004 3:25:38 AM]

Share this post


Link to post
Share on other sites
hey fruny, i appreciate your help.. before this thread dies, i want to make sure im doing this right. i would hate to have a big memory leak like this, especialyl since if i dont ask now ill never know...

ok, so i have in my Enemy class, a static list < Enemy *> enemies_in_world;

in the constructor and copy constructor (but NOT in = op), i do

enemies_in_world.push_back(this);

now, in the destructor i do:

enemies_in_world.remove(this);

so this is cool? remove will search through the list, looking for the value i send as parameters, and when it finds it, it gets removed from the list?



Rather inefficient if you have a number of enemies to remove (I would do a first pass to mark the enemies to remove - with a 'remove-me' flag, and then a second pass with remove_if to remove them all in one go, but that would require modifying your Enemy class).




so, inside of the Enemy destructor, it should look like this:

if(remove_me)
enemies_in_list.erase(this);

but, i dont know if this is cool or not. my Enemies are all created dymanically, stored, and delete via a World_Update class (which manages all of my objects).. my World_Update class doesnt have a remove_me member, and it doesnt need it either (only enemies do really because enemies are the only things i track outside of the list < World_Update*>)

anyway, it wouldnt make sence to make World_Update have a member that only enemies would use, so i guess thats out unless you have a suggestion to keep it elegant.

also, i was under the impression find_if took the same speed as find(), at least tahts what the STL docs say... but anyway, is this what it would look like:

//inside Enemy Destructor
find_if(enemies_in_world.begin(),enemies_in_world.end(),Remove_Me);

//somewhere else
bool Remove_Me(Enemy *e);
{
return remove_me;
}

??



Well, the copy constructor does take a parameter. My copy constructor for Foo does rely on the copy constructor for Data. Again, why would I create a default Data object (with new Data()) only to immediately overwrite its contents with those from*rhs.Data?

well, my Weapon class (Data) doesnt have a copy constructor. i was under the impression a class only needs a CC / = operator if the class uses any dynmic memory... my Weapon class DOES use dynamic memory, but none of it is messed with in either the constructor or destructor. its all managed from member functions... so am i right in doing things like this or do i need a CC?



Then you must destroy the old object. The new object isn't even guaranteed to have the same type. The object's vtable also needs to be properly initialized (which is done by the constructor). Using direct assignment here will be catastrophic.


sorry but, whats a vtable?


Manually checking the types isn't justified here. Just delete the old weapon and create a new one.


ok, but how would i know what kind of weapon to create? i was going to check what type of weapon it was, then delete it, then make a new one based on that weapon.. i mean, what if i delete their weapon (it coul be ANY weapon at this point), how do i know which weapon to allocate now?

sorry for all the questions, thanks a lot for your help!!!

[edited by - graveyard filla on June 11, 2004 9:41:34 PM]

[edited by - graveyard filla on June 11, 2004 9:42:07 PM]

[edited by - graveyard filla on June 11, 2004 9:43:04 PM]

Share this post


Link to post
Share on other sites
remove will search through the list, looking for the value i send as parameters, and when it finds it, it gets removed from the list?

Use erase, not remove. erase searches for a value, remove works with an iterator (i.e. you know where in the list the object to remove is).
edit - oopsie

anyway, it wouldnt make sence to make World_Update have a member that only enemies would use, so i guess thats out unless you have a suggestion to keep it elegant.


Forget it. It would take a different data architecture.

also, i was under the impression find_if took the same speed as find(), at least tahts what the STL docs say...

Your code is correct, though I would use std::list::remove_if instead. Not only is remove_if the function you're looking for in that case, but since std::list provides it as a member function, it is preferable to the general function (as it can be optimized for lists).

well, my Weapon class (Data) doesnt have a copy constructor.

If you don't write a copy constructor, the compiler automatically generates one which does memberwise copy. That is, POD-types undergo bitwise copy, and type with user-defined copy constructors see these constructors called.

i was under the impression a class only needs a CC / = operator if the class uses any dynmic memory...

That's one reason, but that's not necessarily the only valid reason. If you do have a destructor, copy constructor or assigment operator, you generally need all three.

my Weapon class DOES use dynamic memory, but none of it is messed with in either the constructor or destructor. its all managed from member functions... so am i right in doing things like this or do i need a CC?

I don't know. You may be able to get away without it.

sorry but, whats a vtable?

Virtual function table - it's how virtual functions are generally implemented in C++. For each class that has virtual functions, the compiler will generate a table pointing to those functions. Then, in each object of that class, it inserts a pointer to that table - the virtual function table. It also often carries extra type information data.

When you use polymorphic types, the vtable is basically what determines the 'true' type of the object.

ok, but how would i know what kind of weapon to create? i was going to check what type of weapon it was, then delete it, then make a new one based on that weapon..

You want to create a new object base on the other enemy's weapon, not on the one that is overwritten, so deleting the old weapon is no problem.

i mean, what if i delete their weapon (it coul be ANY weapon at this point), how do i know which weapon to allocate now?

With a virtual Clone method, watch the magic:


class Weapon
{
public:
virtual ~Weapon() {}; // Mandatory virtual destructor

virtual Weapon* Create() const = 0;
virtual Weapon* Clone() const = 0;
};

class Shotgun : public Weapon
{
public:
Weapon* Create() const { return new Shotgun; }
Weapon* Clone() const { return new Shotgun(*this); }
};

class RocketLauncher : public Weapon
{
public:
Weapon* Create() const { return new RocketLauncher; }
Weapon* Clone() const { return new RocketLauncher(*this); }
};


And in your enemy copy constructor, you do
   
Enemy(const Enemy& rhs)
: weapon(rhs.weapon->Clone())
{}


Which is how, in C++, you ask "Make me another object of the same type as this one", without yourself knowing the exact type of the object. obj.Create() will create a default object of the same type as obj, and Clone() will create a copy of obj.

I hope it makes sense to you.


“Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.”
— Brian W. Kernighan


[edited by - Fruny on June 13, 2004 8:53:22 AM]

Share this post


Link to post
Share on other sites
thanks fruny,

it does make sence (almost). except, im still unclear on how to go about adding and removing an enemy from the list. please tell me if im doing this right:

inside the Enemy default constructor AND copy constructor AND assignment operator(wrong?):

enemies_in_world.push_back(this);


now inside the Enemy destructor:

enemies_in_world.erase(find(enemies_in_world.begin(),enemies_in_world.end(),this));


thanks again for all your help!!

Share this post


Link to post
Share on other sites
I've skimmed over this thread and I don't think anyone
has suggested this yet.

A fairly easy solution to this problem is to use a
smart pointer that automatically manages whether or not the pointer is valid. Boost::weak_ptr is an example of such a thing.

A weak pointer is similar to a reference counting smart pointer, but instead of owning the resource, it is simply notified when the resource is destroyed and sets it's internal pointer to 0.

For example, your player could keep a list of enemy weak pointers. Whenever a enemy died all weak pointers referencing it would be set to 0. The player can then easily determine which enemies are still alive and remove those that have died.

Share this post


Link to post
Share on other sites

  • Advertisement