pointers sharing dymanic memory

Started by
18 comments, last by d_emmanuel 19 years, 10 months ago
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]
FTA, my 2D futuristic action MMORPG
Advertisement
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]
"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
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!!
FTA, my 2D futuristic action MMORPG
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 constructorFoo b(a);  // Copy constructor Foo c = b; // Copy constructora = 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
"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
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
FTA, my 2D futuristic action MMORPG
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]
"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
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]
FTA, my 2D futuristic action MMORPG
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]
"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
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!!
FTA, my 2D futuristic action MMORPG
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.

This topic is closed to new replies.

Advertisement