Inserting an object into an stl map

Dec 10, 2011 at 6:14am
Hi everyone, I'm new to being registered here (though I've found a few answers from other people's forum posts before), but I am really stumped with the problem I am having.
My final project in my current programming class is to essentially write a video game program, but just one that runs as text in the terminal with no graphics or anything fancy like that, and very very simplistic gameplay. The problem I'm having is that I can't figure out how to store objects of derived classes of my class Character so that they can be accessed and used by multiple functions.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <iostream>
#include <string>
#include <map>
#include "Tyrant.h"
#include "Dagron.h"

using namespace std;

void firstPhase();

int main()
{
	cout << "Starting program in main.\n"
		 << "<insert instructions here>\n";

	cout << "Beginning firstPhase:\n\n";
	map<string, Dagron> characterMap;
	Tyrant Hitler("Hitler", "fire", "red");
	characterMap.insert("Hitler", Hitler);

	char command = 'z';
	while (cin >> command && command != 'm')
	{
		if (command == '/')
			cin.ignore(INT_MAX, '\n');
		switch(command)
		{
		case 'c':
			{
				char type = 'z';
				string name = "name";
				cin >> type >> name;
				if (type == 'd')
					Dagron Boom(name, "fire", "red");

				break;
			}
		case 'r':
			{
				cout << "Testing case r\n";
				break;
			}
		case 'a':
			{
				cout << "Testing case a\n";
				break;
			}
		case 'v':
			{
				cout << "Testing case v\n";
				break;
			}
		default:
			{
				if (command == '/')
					break;
				else
				{
					cout << "I (the computer) am sorry, but that last input\n"
						 << "was unrecognizeable.\n\n";
					break;
				}
			}
		}
	}


So, the first problem I encountered was that after creating an instance of Dagron within my while loop, I couldn't manipulate it or access its functions at all outside of the while loop, and one of this project's requirements is that users "playing" must be able to change things about previously created characters. So then I was trying to figure out a way to store these objects, and I thought I could store them in a map with a string as their key and the object as the mapped value...but I guess that doesn't work? I'm not really sure. :/
The true problem here is just that I don't understand how I'm supposed to allow a user to create a character initially, and then have them be able to modify things about it later on (in what the instructor calls the "second phase"), because after using a loop in which they can create as many characters as they want, those characters are not accessible outside of the while loop as far as I know. :/ Blurg! Help. Please. Thank you ahead of time.
Dec 10, 2011 at 6:37am
Create outside the while loop Dagron Boom; set the parameter of Boom inside the wile loop.

So then I was trying to figure out a way to store these objects, and I thought I could store them in a map with a string as their key and the object as the mapped value...but I guess that doesn't work? I'm not really sure. 


Map should work . try
1
2
map<string, Dagron> characterMap;
characterMap.insert(pair<"Hitler", Hitler>);


Third one

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
cin >> command ;
	while ( command != 'm')
	{
		if (command == '/')
			cin.ignore(INT_MAX, '\n');
		switch(command)
		{
		case 'c':
			{
				char type = 'z';
				string name = "name";
				cin >> type >> name;
				if (type == 'd')
					Dagron Boom(name, "fire", "red");

				break;
			}
		case 'r':
			{
				cout << "Testing case r\n";
				break;
			}
		case 'a':
			{
				cout << "Testing case a\n";
				break;
			}
		case 'v':
			{
				cout << "Testing case v\n";
				break;
			}
		default:
			{
				if (command == '/')
					break;
				else
				{
					cout << "I (the computer) am sorry, but that last input\n"
						 << "was unrecognizeable.\n\n";
						 
					break;
				}
			}
		}
			 cin >> command ;
	} //end of while 
Dec 11, 2011 at 5:50am
The problem with creating Dagron (or a number of other things) outside the while loop is that the user has to be able to create as many dagrons or tyrants etc as they want, so they have to be created after the user has decided to create them, i.e. inside the while loop. (Let me know if my logic seems flawed lol)

I will try that right now...

Is there any particular reason that you suggest setting up my while loop that way? I'm pretty sure both provide the same functionality...

EDIT: Also this reminded me of other questions I have that I can't seem to figure out...
1. How am I supposed to create new class names every time the user creates a new dagron or tyrant or other object? Like, if the user enters the command to create a dagron named boom, how could I make it possible for the program to know to create an object of type Dagron with the actual object name as Boom? And then if the user then enters the command to create a second dagron named Rawr, ideally it would be created as Dagron Rawr("Rawr", etc, etc) but how would my program know to use Rawr as the name of the object?

2. If I want my characterMap to be a map of all the different characters the user creates, including dagron and tyrant (which are derived classes of derived classes of the class character that I created), can I initialize the map like map<string, Character> characterMap? Because I tried to do that before and it didn't work. Though maybe it will now using the pair.

Sorry I'm so clueless, your help is GREATLY appreciated :)
Last edited on Dec 11, 2011 at 5:58am
Dec 11, 2011 at 7:36am
Ok I've got a new question now...I figured out the pair deal, I used make_pair() instead of just pair<> and now I can successfully create a map of as many different pointers to characters as I want. However....
Now, the problem is sort of complicated:
I want the map to store Dagron objects And Tyrant objects, both of which are derived classes of Character, which is an abstract class. Both Dagron and tyrant have functions that are not present in Character (obviously).
So, I have the map initialized as map<string, Character*> so that the program can store pointers to derived classes of Character in the map.
However, if, after creating a Dagron object and storing a pointer to it in the map, I try to call a function of that Dagron that is not a function in Character, the compiler gives me the error
gameDriver.cpp(76) : error C2039: 'rescue' : is not a member of 'Character'
        c:\programming\Character.h(24) : see declaration of 'Character'


How to fix...? Again, thank you for your help.

Edited Dec. 11 1:20pm

wow, I just realized that I used the word pointer instead of pair up there...fail....changed it now.
Last edited on Dec 11, 2011 at 9:21pm
Dec 11, 2011 at 8:31am
Make a pure virtual function in Character that the derived classes override.
Dec 11, 2011 at 9:04pm
I think I would prefer not to have to do that, mainly because I feel like that would somewhat ruin the correct base-class to derived-class relationship....i.e., the functions in the Character class are functions that any derived class will have, but the functions in the derived classes are not functions that every character will have. Because Tyrant has one specific function that is special to itself, and Dagron has one specific function that is special to itself, so it seems like it would just be wrong to have to put virtual functions of those in the Character class, because not all characters have those capabilities. And I might get points marked off for that.
Dec 11, 2011 at 9:38pm
I want the map to store Dagron objects And Tyrant objects,
¿why? ¿can't just use 2 different maps?

Another option is to cast. You will need a way to know if the object is a Dagron (¿what is that?)
Dec 11, 2011 at 10:09pm
Yeah I just figured out how to make casting work, using static_cast. Was trying to get that working for a while.

Dagron is really just a dragon, but I wanted to spell it funky haha.
Dec 12, 2011 at 1:54am
Ok NOW I have an even different problem...the static_cast is working, sort of, but when I try to call a function from within Dagron or Tyrant called displayStatistics that just displays all information about the object, like its name, a special ability, its stats, etc, some things get left as blank, even though they've been inputted properly. Here's my code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
int main()
{
	cout << "Starting program in main.\n"
		 << "<insert instructions here>\n";

	cout << "Beginning firstPhase:\n\n";
	map<string, Character*> characterMap;
	map<string, Character*>::iterator mapIter;

	char command = 'z';
	while (cin >> command && command != 'm')
	{
		if (command == '/')
			cin.ignore(INT_MAX, '\n');
		switch(command)
		{
		case 'c':
			{
				char type = 'z';
				string name = "name";
				cin >> type >> name;
				cout << "type is " << type << " and name is " << name << endl;
				if (type == 'd')
				{
					Dagron userDagron(name, "n/a", "unknown");
					userDagron.displayStatistics();
					Character *dagronPtr = &userDagron;
					characterMap.insert(make_pair(name, dagronPtr));
				}
				else if (type == 't')
				{
					Tyrant userTyrant(name, "n/a", 0);
					Character *tyrantPtr = &userTyrant;
					characterMap.insert(make_pair(name, tyrantPtr));
				}
				break;
			}
		case 'r':
			{
				string rescuer, victim;
				int pointValue = 0;
				cin >> rescuer >> victim >> pointValue;
				mapIter = characterMap.find(rescuer);
				if (mapIter != characterMap.end())
				{
					Dagron *dagronPtr = static_cast<Dagron*>(mapIter->second);
					dagronPtr->rescue(victim, pointValue);
				}
				break;
			}
		case 'a':
			{
				string attacker, victim;
				cin >> attacker >> victim;
				mapIter = characterMap.find(attacker);
				if (mapIter != characterMap.end())
				{
					Tyrant *tyrantPtr = static_cast<Tyrant*>(mapIter->second);
					tyrantPtr->attack(victim);
				}

				break;
			}
		case 'v':
			{
				string character;
				cin >> character;
				mapIter = characterMap.find(character);
				if (mapIter != characterMap.end())
				{
					mapIter->second->displayStatistics();
					cout << endl;
				}
				break;
			}
		default:
			{
				if (command == '/')
					break;
				else
				{
					cout << "I (the computer) am sorry, but that last input\n"
						 << "was unrecognizeable.\n\n";
					break;
				}
			}
		}
	}


I was supposed to turn this assignment in on Friday but I've been far too lazy this semester and so I'm still not finished with it.... :( I hate when I'm lazy. I just wish I could figure this crap out on my own.
Dec 12, 2011 at 8:04am
Alright. I've finally got it down to ONE single problem that my code has. I have a map of pointers to objects. That all works fine, I can insert the pointers just fine. The problem is when trying to call functions of the objects to which the pointers in the map point. For example, if I have created a Dagron, and inserted a pointer to it into my map, and then I get an iterator to that pointer, when I try to do something like mapIter->second->displayStatistics(); I get a segmentation fault when using g++ in xcode. I have literally been searching Google for the past two hours trying to figure out how the hell to use the iterator to the pointer in my map so that I can access the functions of the object the pointer points to, but EVERYTHING I've tried has either not compiled or given a segmentation fault once I get to that part of the program. I read somewhere that because it's an iterator to a pointer, you simply can't use the -> to access the map value (the pointer), but I don't understand what else I'm supposed to do....someone PLEASE help. I'd prefer not to fail this class at the very end of the semester.
Dec 12, 2011 at 8:14am
mapIter->second->displayStatistics();


You may want to check if your insertion of pointers into the map is actually fine or not. You can test by using below.

if (mapIter->second) mapIter->second->displayStatistics();
else cout << "detect null in value!\n";
Dec 12, 2011 at 8:27am
When you try to cast to a Dagron pointer are you sure the object pointed to actually is a Dagron object?
In these cases dynamic_cast is often to prefer because it can tell you if the cast was valid (I think it only works if the base class has at least one virtual function).
Dec 12, 2011 at 8:36pm
Scope
1
2
3
4
5
6
7
				if (type == 'd')
				{
					Dagron userDagron(name, "n/a", "unknown");
					userDagron.displayStatistics();
					Character *dagronPtr = &userDagron;
					characterMap.insert(make_pair(name, dagronPtr));
				}//here userDagron dies 
You've got a garbage pointer.
In this case it may be a good idea to use new transfering the ownership to the container.
Don't forget to delete or use an smart pointer
Dec 12, 2011 at 9:41pm
@sohguanh I tried what you suggested and the same thing still happens.

@ne555 could you explain to me further how I would use new and delete in my code properly?

The reason I thought my pointers were working at least partially is because I Was using Microsoft visual c++ 2010 express and that didn't give a segmentation error at all, it compiled with no errors, and then at run, when it got to displayStatistics, it did call that function, but with some of the fields blank as if no data had been stored, or with very strange characters, like corrupt data. I figured that yes, the pointer was pointing to a Dagron, but somewhere along the way to that function call it was getting screwed up. And now here in xcode I get the segmentation fault.
Dec 13, 2011 at 12:39am
Ha-ha! Thank you ne555 for your suggestion, I have gotten it working properly now, no segmentation fault and no corrupted or missing data in the function! :D (Although I haven't exactly figured out where to use delete yet so I am sure there's a teeny weeny memory leak on my computer right now :P)

Now a new question....yes, aren't questions wonderful?

I am trying to store, inside my Villain and Hero class, a list of strings that that particular character has attacked or rescued, respectively, and I'm trying to use std::list<> to do i. I have a list declared as a private member in the .h file, but in the .cpp file for the class, when I try to use the list, the compiler tells me
'attackList' was not declared in this scope
even though it is part of the same class....? I hate this program so much right now...
Dec 13, 2011 at 2:31am
You delete them when you stop using them. By instance before you erase it from the map.
It may be better to let a wrapper handle that in the destructor. Use an smart pointer, like std::auto_ptr

Need to see the code for the other question. Maybe it's just a missing include.
Dec 13, 2011 at 2:52am
In fact it was a missing include. I'm now having another problem where trying to push_front a string into the list doesn't seem to work. I have two separate derived classes in which I've written the same code, but it behaves differently in each one...I was getting a termination to do with std::length_error for a while, and then one of them was giving me a bus error when reaching that point...and sometimes one of them will just act like it worked but then when calling displayStatistics() it doesn't show anything listed as having been stored in the list. I just don't know. Here's the code for Hero (the class Dagron is derived from):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include "Hero.h"
#include <iostream>

using namespace std;

Hero::Hero(): specialAbility("n/a"), Character("Hero")
{
}	//end default constructor

Hero::Hero(string heroName, string newAbility): Character(heroName)
{
	if (newAbility.size() > 0)
		specialAbility = newAbility;
	else specialAbility = "n/a";
}	//end constructor

string Hero::getAbility() const
{
	return specialAbility;
}	//end getAbility

void Hero::setAbility(string newAbility)
{
	if (newAbility.size() > 0)
		specialAbility = newAbility;
	else return;
}	//end setAbility

void Hero::rescue(string rescuedChar, int rescuePoints)
{
	cout << "Hero " << getName() << " rescued "
		 << rescuedChar << " and got " << rescuePoints
		 << " points for it!\n";
	Hero::addToRescueList(rescuedChar);
}	//end rescue

void Hero::addToRescueList(string rescuedChar)
{
	if (rescuedChar.size() > 0)
	{
		rescueList.push_back(rescuedChar);
		cout << "Done with push_front\n";
	}
	else return;
}

void Hero::displayStatistics() const
{
	Character::displayStatistics();
	cout << "\nSpecial Ability: " << getAbility();
	cout << "\nThis hero has rescued:";
	for (list<string>::const_iterator it = rescueList.begin(); it != rescueList.end();
		 ++it)
		cout << endl << *it;
}	//end displayStatistics
//end implementation file 


and for Villain (the class Tyrant is derived from):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>
#include "Villain.h"

using namespace std;

Villain::Villain(): evilAbility("n/a"), Character("Villain")
{
}	//end default constructor

Villain::Villain(string villainName, string newAbility): Character(villainName)
{
	if (newAbility.size() > 0)
		evilAbility = newAbility;
	else evilAbility = "n/a";
}	//end constructor

string Villain::getAbility() const
{
	return evilAbility;
}	//end getAbility

void Villain::setAbility(string newAbility)
{
	if (newAbility.size() > 0)
		evilAbility = newAbility;
	else return;
}	//end setAbility

void Villain::attack(string attackedChar)
{
	cout << "Villain " << getName() << " attacked character "
		 << attackedChar << ". Oh no!\n";
	Villain::addToAttackList(attackedChar);
}	//end attack

void Villain::addToAttackList(string attackedChar)
{
	if (attackedChar.size() > 0)
		attackList.push_front(attackedChar);
	else return;
}	// end addToAttackList

void Villain::displayStatistics() const
{
	Character::displayStatistics();
	cout << "\nEvil ability: " << getAbility();
	cout << endl << "This villain has attacked:";
	for (list<string>::const_iterator it = attackList.begin(); it != attackList.end();
		 it++)
		cout << endl << *it;
}	//end displayStatistics
//end implementation file 
Dec 13, 2011 at 6:19am
Ok I either fixed or got around most of the problems I had, but now there's one final thing....
The instructor said we have to have it set up so that the "firstphase" can be done with piped input at runtime, but I'm pretty sure the second phase is supposed to be seen by the user on the screen. So, how can I code this driver so that after the first phase is over, it will stop receiving piped input from a file, and start receiving regular input from the user in the terminal/console? (Because right now if I pipe a file in at runtime, after going through first phase it causes an infinite loop.)

Thanks for your help, everyone who helped
Dec 13, 2011 at 6:46am
how criticle this is !!!!!
Topic archived. No new replies allowed.