Multimap .first, .second?

Started by
20 comments, last by MaulingMonkey 16 years, 11 months ago
I was looking at this tutorial on multimaps and I noticed that near the bottom, there is this:

cout << (*it).first << " => " << (*it).second << endl;
Where it is an iterator. But where is this .first and .second coming from? I don't see it anywhere if I push ctrl+space in VC++, but the strange thing is it seems to work. I think that it get the "key" and "value" from the map but I can't be sure. I'm storing a pointer as the value in a multimap, and when I do this:

(*iter).second->Update();
It runs the "Update" function in the object. But if I do this:

if ((*iter).second)
{
...
It always seems to return true (I'm trying to check if the pointer is valid). I can't seem to find anything in the tutorial actually defining the "second" function, or telling why it is there or where it is "from". I can't find it in other tutorials either. What is this about? What could I use to check if an iterator in a multimap "points" to a valid pointer (if that makes sense)? Thanks
Advertisement
Map/Multimap are actally sets/multisets of std::pair, keyed off of the first element.

std::pair<int, int> intPair(3, 4);int x = intPair.first; // 3int y = intPair.second; // 4


So when you iterate over them, you can refer to
iter->first
(the key) and
iter->second
(the value).
map and multimap store objects of type std::pair<KeyType,ValueType>. std::pair is a simple aggregate that exposes two member fields: first and second. In the given context, first is the key and second is the value (a pointer in your case).

'(*iter).second' should evaluate to false if the specified pointer is null. If this doesn't seem to be working, you might try displaying the value of the pointer before testing it for nullity (maybe the pointers you think are null are not actually null).

Also, I personally find it->first to be a little easier to read than (*it).first, but that's just personal preference.
Thanks for clarifying. Now that I know what they do I'll probably be able to figure out what is wrong.

EDIT: This doesn't seem to work...
if (iter->second)

It always returns true, even if I deleted the object that the pointer points to.

[Edited by - Gumgo on May 4, 2007 10:44:42 PM]
I don't believe 'delete' is required to assign NULL, so if(iter->second) would still point at the (now free) memory.
Quote:Original post by Replicon
I don't believe 'delete' is required to assign NULL, so if(iter->second) would still point at the (now free) memory1.


Okay I believe it might even be explicitly required not to by the standard -- I certainly know of no implementation that does assign a pointer to NULL. Note that such a change would only affect one copy of the pointer -- and if you've only got one pointer, one has to wonder why you were using pointers in the first place. An object that has been deleted and a pointer which points at nothing (e.g. "point at NULL") are unrelated, orthogonal concepts.

Gumgo: Your noteworthy options are:

1) NULL the pointer yourself.
2) Remove the entry from the multimap.
3) Use a boost::weak_ptr (in conjunction with boost::smart_ptr).


[1] Terminology: This is known as a "dangling pointer".
I think I know what I'm going to do then in this case. Rather than delete the object, I'm going to set a variable in the object (maybe "DELETE_ME") that gets set when I want to delete it. Then, all the containers that have a pointer to that object will detect if DELETE_ME is true and if so, rather than updating the object, it will be removed from the container (I have several different containers). Then after all the updating has been done, I'll run another function to "clean up", which will delete all objects with DELETE_ME set to true.

That sound like it will work? :)

EDIT: Hehe, just noticed that "DELETE_ME" is actually taken...

Aaargh! This is getting really irritating! I can't get ANY of this to work!

if ((iter->second)->DEL_ME == true)


iter is an iterator in a multimap. The multimap contains objects with the variable DEL_ME. This keeps returning an exception!

What I'm confused about is how do you access the FUNCTIONS and VARIABLES of an object using an iterator.

[Edited by - Gumgo on May 5, 2007 3:33:12 AM]
Quote:Original post by Gumgo
That sound like it will work? :)

As long as you keep one pointer of the object around to do the actual delete with somewhere.

Quote:
if ((iter->second)->DEL_ME == true)


iter is an iterator in a multimap. The multimap contains objects with the variable DEL_ME. This keeps returning an exception!

What I'm confused about is how do you access the FUNCTIONS and VARIABLES of an object using an iterator.


... You're accessing the variable in that above snippet just fine, syntaxically. For that to have problems, either:

1) iter is an invalid iterator
2) iter->second is an invalid pointer

Note that removing elements from containers typically invalidates iterators (not necessairly just the ones that reference the object(s) removed, although I think that may be the case for multimap at least). Dangling pointers (as mentioned earlier) are also, of course, invalid.

In conclusion: Paste more source code. And pasting exact error messages never hurts either.
Quote:Hehe, just noticed that "DELETE_ME" is actually taken...
By whom? Also, why all caps?
Quote:
if ((iter->second)->DEL_ME == true)
There's no need to compare to true here. Just write:
if (iter->second->DEL_ME)
Quote:iter is an iterator in a multimap. The multimap contains objects with the variable DEL_ME. This keeps returning an exception!
You'll need to be more specific about the errors you're encountering. If you mean an exception is being thrown, then you need to post the message (if any) associated with the exception, and/or its type (if you know it). If it's an assertion failure, show the actual code that fails. If it's a compile-time error, post the error message along with the code that generates it. If your program is crashing, use the debugger to find out where and why. If it's simply not behaving correctly, describe the incorrect behavior and post the relevant code. And so on.
Quote:What I'm confused about is how do you access the FUNCTIONS and VARIABLES of an object using an iterator.
If the value type of your map is a pointer, then:
iter->second->my_function_or_variable
Is correct.

As for your 'delete me' flag, it's probably not a good idea (at least not as you described it). It's very brittle, and will likely lead to dangling pointers and various other unpleasantries.

An appropriate solution will most likely involve using smart pointers in some way, as mentioned previously. If you want more specific feedback, you may need to provide more information (such as why it's necessary to store the same object in multiple containers, and what these various containers are used for).
Sorry. I seem to have a problem with not providing enough code...

Quote:
By whom? Also, why all caps?

I'm not sure, but I just noticed that it was on the list when I hit "ctrl+shift" already (#define DELETE_ME). But that isn't important cause I changed it.

Anyways, the reason of doing this is because I want to delete objects only when they are removed from ALL loops that may need to access them (or have a pointer to them). There is a render loop and updating loop, and if I delete one from the updating loop and delete the pointer to it from the loop as well, the pointer in the render loop may point to something different by the time it comes along.

Anyways, here's the code:
class ObjectClass{public:	bool DEL_ME;	int depth;	virtual void Update() = 0; // abstract base	virtual void Draw() = 0; // abstract base};...class GameLoopClass{public:	DWORD ObjectNumber; // don't bother telling me to just check the size of the multimap, I'll delete this (it was from something from before)	std::multimap<int, ObjectClass*> ObjHolder; // int = depth	GameLoopClass();	void AddObj( ObjectClass * object );	void Update();};...GameLoopClass::GameLoopClass(){	ObjectNumber = 0; // yes I know it is pointless I'll take it out}void GameLoopClass::AddObj( ObjectClass * object ){	ObjHolder.insert( std::pair<int, ObjectClass*>( object->depth, object ) );	ObjectNumber++;}void GameLoopClass::Update(){	std::multimap<int, ObjectClass*>::iterator iter;	for (iter = ObjHolder.begin(); iter != ObjHolder.end(); iter++)	{		if (iter->second->DEL_ME != true)			iter->second->Update();	}	// reason for two loops is that I want all the updating to happen	// and then I want to get rid of the pointers. The reason is,	// say I had OBJECT1 get updated. Then OBJECT2 gets updated, and in	// its updated code, it sets DEL_ME to true for OBJECT1. OBJECT1 would	// have already been updated but would have been set to be deleted	// AFTER that, and the pointer to it would not be deleted from the	// multimap	for (iter = ObjHolder.begin(); iter != ObjHolder.end();)	{		if (iter->second->DEL_ME)		{			ObjHolder.erase( iter );			ObjectNumber--; // one less object		}		else			iter++;	}}


There is my complete code (it is very similar in the Render loop, and also the "clean up" loop that I run afterwards to actually delete the objects.

This topic is closed to new replies.

Advertisement