Sign in to follow this  

Recommended Posts

I realized yesterday that a list would be more appropriate for the circumstances than a vector would be. So I use to have vector< Bot* > bots; which I replaced with: list< Bot* > bots; now I use to do this:
for(int i = 0; i < (signed)bots.size(); i++)
    delete (Bot*)(bots[i]);

can I do this for a list:
while(!bots.empty())
{
    delete *(bots.begin());
    bots.remove(bots.begin());
}

Is that legal? I am a little confused if an iterator is a pointer to the object in the list, or the node in the list. Thanks in advance!

Share this post


Link to post
Share on other sites
ToohrVyk    1596
An iterator works like a pointer to the element in the list. The element in the list is assured to be a valid element as well.

However, this is not relevant at all in your case, since the elements of your list are pointers. Therefore, your second method is perfectly correct, since you're manipulating pointers, and not actual objects.

EDIT: however, the above code can be made better in two ways:

- It will crash if you add the same pointer to the list twice (since it will be deleted twice).
- You can use pop_front to remove the first element in a more optimized manner.
- If you intend to remove all elements from the list, call list.clear( ) instead of doing it yourself manually.
- You could even use for_each with a correct functor or function to delete all objects, instead of doing it yourself.

Share this post


Link to post
Share on other sites
Thanks ToohrVyk! My other question is, would you suggest that I use

list<Bot&> bots;

that way in my functions, instead of


bool someFunc(Bot* p)
{
if(p)
... do something
}



I can do it like


bool someFunc(Bot& p)
{

... do something
}



and not have to check for validity of pointer. The reason I ask is because I took it to some profs, and they looked at me like they were going to vomit when they saw the pointers, and said that nowadays it would make more sense to use a reference.

Share this post


Link to post
Share on other sites
pragma Fury    343
Quote:
Original post by Sagar_Indurkhya
Thanks ToohrVyk! My other question is, would you suggest that I use

list<Bot&> bots;

that way in my functions, instead of

*** Source Snippet Removed ***

I can do it like

*** Source Snippet Removed ***

and not have to check for validity of pointer. The reason I ask is because I took it to some profs, and they looked at me like they were going to vomit when they saw the pointers, and said that nowadays it would make more sense to use a reference.


Your list would have to be defined "list<Bot> bots".
Unless your bots are declared elsewhere and you really only want your list to contain references to those bots.

Even if your list is of Bot*'s, you can still have your functions accept a Bot&. You simply have to dereference the value from your list


typedef std::list<Bot*> BotList;
BotList bots;

for(BotList::iterator itr=bots.begin(); itr != bots.end(); ++itr)
{
someFunction(*(*itr));
}

...
void someFunction(Bot &bot){...}







Or, if your function accepted a reference to a pointer, you would at least not have to worry about explicit NULL pointers.


void someFunction(Bot*& pBot){...}

// elsewhere..

BOT *pBot = new Bot();
someFunction(pBot); // fine
someFunction(NULL); // error C2664

// doesn't stop this from happening though
BOT *pNullBot = NULL;
someFunction(pNullBot); // no compiler errors/warnings.







I'm not sure what issues your profs have with pointers, but IMHO, pointers are a pivotal feature of the C++ language. References have their uses, yes, but so do pointers. If you look at managed C++, you have to use pointers EVERYWHERE you want to use a .NET object. You cannot allocate one on the stack.

Regarding your original post, you might want to delete your list of Bot*'s thusly:

typedef std::list<Bot*> BotList;
BotList bots;

//...

for(BotList::iterator itr = bots.begin(); itr != bots.end(); ++itr)
{
delete (*itr);
}
bots.clear();





Share this post


Link to post
Share on other sites
snk_kid    1312
Quote:
Original post by pragma Fury
if your function accepted a reference to a pointer, you would at least not have to worry about explicit NULL pointers.


void someFunction(Bot*& pBot){...}
//....
someFunction(NULL); // error C2664



Just pointing out that is only because its a non-constant reference, its totally valid to assign temporaries and/or literals to constant references. That is assuming NULL is defined to be a literal 0.

Quote:
Original post by pragma Fury
If you look at managed C++, you have to use pointers EVERYWHERE you want to use a .NET object. You cannot allocate one on the stack.


In C++/CLI you can do, even more so for value types, but i know what your getting at. Languages like java, C#, Managed C++, C++/CLI are biased towards reference semantics, while standard C++ is biased towards value semantics.

@Sagar_Indurkhya:

I thought i'd let you know just incase you didn't realize std::list::remove/remove_if removes all occurrences of some given value/criteria that might be something you may or may not want.

If your going to store pointers in standard library containers i recommend you have look up reference counted/linked smart pointers, boost library has a family of smart pointers you can use.

Also another thing useful is boost iterator library in particular boost's indirect_iterator where it makes a container of (smart) pointers to some type look like a container of the actual type.

Lastly in general prefer using the standard library generic algorithms over handwritten explicit loop code like your deleting code.

Share this post


Link to post
Share on other sites
ok, I incorporated list instead of vector for some stuff. Here are some questions that I have:

My Lists look like this:

list<Bot> bots;
list<Bullet> bullets;
list<InformationWave> waves;



Can I delete them like this?

Environment::~Environment(void)
{
// CLEAN UP EVERYTHING
bots.clear();
bullets.clear();
waves.clear();
}



Also, something I have been wondering about:

for(list<Bullet>::iterator iter = bullets.begin(); iter != bullets.end();)
{
for(list<Bot>::iterator jiter = bots.begin(); jiter != bots.end;)
{
// The bullet hit a bot
if(iter->KillsBot(jiter->getX(), jiter->getY(), Constants::BOT_RADIUS))
{
// Decrease the bots health, and remove it if necessary
if(bots[j]->DecrementHealth() == 0)
jiter = bots.remove(jiter);

// Remove the bullet
iter = bullets.remove(iter);
break;
}
else
jiter++
}
}



Basically, I iterate through each bullet, see if it hits a bot. If it does, decrement the bot, and if it is dead, remove it. Then remove the bullet. My problem is how should I know when to iterate the iterator for the bullets.

Share this post


Link to post
Share on other sites
pragma Fury    343
Quote:
Original post by Sagar_Indurkhya
Can I delete them like this?
*** Source Snippet Removed ***

yep.

Quote:
Original post by Sagar_Indurkhya
Also, something I have been wondering about:
*** Source Snippet Removed ***

Basically, I iterate through each bullet, see if it hits a bot. If it does, decrement the bot, and if it is dead, remove it. Then remove the bullet. My problem is how should I know when to iterate the iterator for the bullets.


You could do something simple, like use a boolean flag.

bool bHitBot;
for(list<Bullet>::iterator iter = bullets.begin(); iter != bullets.end();)
{
bHitBot = false;
for(list<Bot>::iterator jiter = bots.begin(); jiter != bots.end;)
{
// The bullet hit a bot
if(iter->KillsBot(jiter->getX(), jiter->getY(), Constants::BOT_RADIUS))
{
// Decrease the bots health, and remove it if necessary
if(bots[j]->DecrementHealth() == 0)
jiter = bots.remove(jiter);

// indicate the bullet hit the bot
bHitBot = true;
break;
}
else
++jiter;
}

// if the bullet hit a bot, remove it.
if(bHitBot)
iter = bullets.remove(iter);
else
++iter;
}






Also note that I changed your iterators to use the pre-increment operator. The post-increment operator will make a copy of your iterator, return that, and increment your original. Since you don't need that copy, the pre-increment will be faster.

Share this post


Link to post
Share on other sites
Omaha    100
I personally have never seen the C++ reference operator used for variable declaration, only as a function parameter decorator. If you don't want to hold on to the object itself, I'd just use a pointer, and then if you have a function that needs (for some crazy reason) the object itself or better, a reference to it, just dereference that pointer (making sure of course that it is valid.)

Additionally, using the reference operator with variables themselves can very easily lead to aliasing confusion. With pointers at least you have the distinction of ptr and *ptr. With references everything is just a name, and changing one will change whatever it's bound to, as in classic example, z.b.:


int x, &y=x;

x=4;

printf("x=%d, y=%d\n", x, y);

y=9;

printf("x=%d, y=%d\n", x, y);


That is a degenerately simple example, but in longer code you might see x and y and first of all wonder why someone declared a variable that has a considerable scope with just a one letter name and no context, and second of all assume that they are independent when in fact they're the same thing. (Ok well technically x is an integer and y refers to x but let's not niggle, even though that's what programmers love to do when put in the same room with one another.) And thus change one and then the other and then pull a double take when you find out that things are not working and only after some code spelunking do a Charleton Heston Planet of the Apes "It was Earth all along!" primal scream of realization when you find out that in fact you were contradicting your own code.

Meanwhile, with pointers, you have to explicitly dereference them if you want to do assignment, and pointers by definition are typically known to be used as indirect ways of getting at other data so when you start seeing unary asterisks in front of names, you know that somewhere else, something is afoot, and that there's more to that particular code than meets the eye. ("Far pointers! Variables in disguise!")

Just my completely irrelevant thoughts on the subject. Summary:

  • Using references for actual variables isn't good
  • Passing objects by reference is good, but so is passing them by reference by passing a pointer and which one to use is really up to you and the context
  • Pointers are awesome and Java programmers are missing out on so much fun
  • I came up with a nerdy remix of the Transformers themesong involving C++


Play nice, kids, and remember--use protection!

Share this post


Link to post
Share on other sites
snk_kid    1312
Quote:
Original post by Sagar_Indurkhya
ok, I incorporated list instead of vector for some stuff. Here are some questions that I have:

My Lists look like this:


list<Bot> bots;
list<Bullet> bullets;
list<InformationWave> waves;


Can I delete them like this?

Environment::~Environment(void)
{
// CLEAN UP EVERYTHING
bots.clear();
bullets.clear();
waves.clear();
}



If those lists are members of your "Environment" class you do not need to do that, the standard library containers handle there own memory management, they have properly defined destructors.

Quote:
Original post by Sagar_Indurkhya
it turns out that I can't do this:

iter = bots.remove(iter); on a list

instead I have to do something like:

list<Bot>::iterator i = --iter;
bots.remove(++iter);
iter = i;


Okay you either didn't read my previous post or you didn't understand me, std::list::remove/remove_if removes all occurrences of some given key value/criteria, its a linear time operation.

From glancing at yours & pragma Fury previous code it looks like you don't want that you already know exactly which specific element you want to erase, just use std::list::erase instead, i.e.


itr = bots.erase(itr);


Quote:
Original post by Omaha
// blah, blah, blah


[headshake][rolleyes] ...

Share this post


Link to post
Share on other sites
Yeah, but from what I saw, it turns out that

std::list::erase(..) returns void

which I found here:

http://www.sgi.com/tech/stl/List.html

and here:

http://www.msoe.edu/eecs/ce/courseinfo/stl/list.htm

So I don't see how I could do:

iter = bots.remove(iter);

since the std::list::erase(const &T) doesn't return anything.

Share this post


Link to post
Share on other sites
snk_kid    1312
Quote:
Original post by Sagar_Indurkhya
Yeah, but from what I saw, it turns out that

std::list::erase(..) returns void

which I found here:

http://www.sgi.com/tech/stl/List.html


Your reading it wrong, it takes an iterator and returns an iterator, look a again.

Quote:
Original post by Sagar_Indurkhya
and here:

http://www.msoe.edu/eecs/ce/courseinfo/stl/list.htm


That reference is incorrect or out of date.

Quote:
Original post by Sagar_Indurkhya
since the std::list::erase(const &T) doesn't return anything.


Well that is not even the correct signature, std::list::erase takes iterators (has two overloads) and returns an iterator.

Share this post


Link to post
Share on other sites
pragma Fury    343
Quote:
Original post by Sagar_Indurkhya
it turns out that I can't do this:

iter = bots.remove(iter); on a list

instead I have to do something like:

list<Bot>::iterator i = --iter;
bots.remove(++iter);
iter = i;


Don't do that... do this:


if(remove the bot)
bots.remove(iter++);
else
++iter;


If you look at the source for the iterator, you see that the post-increment operator looks like this:
iterator operator++(int)
{
iterator _Tmp = *this;
++*this;
return (_Tmp);
}


Whereas the pre-increment operator is:
iterator& operator++()
{
_Ptr = _Acc::_Next(_Ptr);
return (*this);
}


So, iter++ creates a copy of itself, then calls it's own pre-incrementer, and then returns the un-incremented copy. So it's perfectly safe to remove/erase an element out of a STL container using it... the original iterator you're using in your list will still be valid.
And if you just want to increment an iterator outside of an erase/remove, just use ++iter, and save the copy.


Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this