STL List

Started by
11 comments, last by pragma Fury 18 years, 10 months ago
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);

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!
Advertisement
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.
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.
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); // finesomeFunction(NULL); // error C2664// doesn't stop this from happening thoughBOT *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();
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.
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.
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.
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!
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;
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] ...

This topic is closed to new replies.

Advertisement