I've been working on a large project and have been running into a runtime error. I'm using Codeblocks portable 8.02 on Windows 7 with the GNU GCC compiler. I'm using a vector of objects and have not had a problem using push_back until I started using a vector of strings in the vector of classes, which gives me a runtime error ("[name of application] has stopped working. Windows is checking for a solution to the problem."). I've managed to reproduce the bug with the following lines of code:
#include <iostream>
#include <vector>
#include <string>
usingnamespace std;
class C
{
public:
vector<string> mystrings;
};
int main()
{
vector<C> Objects;
Objects.push_back(C());
Objects[0].mystrings.push_back("Hello");
Objects[1].mystrings.push_back("World"); //take this line of code out and it works fine
Objects.push_back(C()); //here's where it breaks
return 0;
}
I believe that the problem occurs when mystrings allocates more memory while the Objects vector does not, but have no idea how to fix it. Ideally, the solution wouldn't include pointers as I'm not very experienced with them yet - but if it does, then I guess I'll have to learn a bit more ;).
Also: I tested this with arrays replacing the vectors, and the same problem occurred. If possible, an explanation of why the solution works would be great; thanks in advance!
The compiler issues an error:
error: no match for 'operator*' in '*Objects'.
If I try switching that to a reference (replacing (*Objects) with (&Objects)), I am warned that there is no member of vector<C*> named mystrings...Thanks for the quick reply, by the way.
character * newCharacter();
class characterSet {
vector<int> x;
list<character*> characters;
list<character*>::iterator it;
character *temp;
string name;
int selection, dice, tempInt;
ofstream mySaveFile;
ifstream myOpenFile;
public:
void showMenu();
};
void characterSet::showMenu(){
cout << "***************************" << endl;
cout << "* Make a selection: *" << endl;
cout << "* 1) New Character *" << endl;
cout << "* 2) Delete Character *" << endl;
cout << "* 3) Save Characters *" << endl;
cout << "* 4) Open Characters *" << endl;
cout << "* 5) Print Characters *" << endl;
cout << "* 6) Roll Dice *" << endl;
cout << "* 0) Exit *" << endl;
cout << "***************************" << endl;
cout << "Menu$ ";
cin >> selection;
switch (selection){
case 1:
temp = newCharacter();
characters.push_back(temp);
it = characters.begin();
while(it != characters.end()){
(*it)->printStats();
++it;
}
break;
case 2:
cout << "Enter the character name to delete." << endl;
cout << "Delete$ ";
cin >> name;
it = characters.begin();
while(it != characters.end()){
if((*it)->charName == name){
delete *it;
it = characters.erase(it);
}
else{++it;}
}
break;
case 3:
mySaveFile.open("save.txt");
it = characters.begin();
while(it != characters.end()){
mySaveFile << " " << (*it)->charName << " " << (*it)->ringVoid << " " << (*it)->statStam << " " << (*it)->statWill << " " << (*it)->statRef << " ";
mySaveFile << (*it)->statInt << " " << (*it)->statStr << " " << (*it)->statPercp << " " << (*it)->statAgil << " " << (*it)->statAware;
++it;
}
mySaveFile.close();
break;
case 4:
myOpenFile.open("save.txt");
it = characters.begin();
while(!myOpenFile.eof()){
myOpenFile >> name;
for(int i = 0; i < 9; i++){
myOpenFile >> tempInt;
x.push_back(tempInt);
}
temp = new character(name, x);
characters.push_front(temp);
x.clear();
}
myOpenFile.close();
break;
case 5:
cout << "Enter the character to print." << endl;
cout << "Print$ ";
cin >> name;
it = characters.begin();
while(it != characters.end()){
if((*it)->charName == name){
(*it)->printStats();
++it;
}
else{++it;}
}
break;
case 6:
cout << "Enter the character to roll for." << endl;
cout << "Roll$ ";
cin >> name;
it = characters.begin();
while(it != characters.end()){
if((*it)->charName == name){
dice = diceRoller((*it)->statStam, (*it)->statWill);
cout << "Rolled a " << dice << endl;
++it;
}
else{++it;}
}
break;
case 0:
exit(0);
break;
default:
cout << "Invalid choice" << endl;
break;
}
showMenu();
}
//This public function gets variables for the character name and stats from the user, creates a pointer to a new character, and passes the new character pointer back to the menu function.
character * newCharacter(){
vector<int> stats;
int temp;
string name, statNames[9] = {"Void Ring", "Stamina", "Will", "Reflex", "Intelligence", "Strength", "Perception", "Agility", "Awareness"};
cout << "Character Name:" << endl << "New$ ";
cin >> name;
for(int i = 0; i < 9; i++){
cout << "Enter the character's " << statNames[i] << "." << endl << "New$ ";
cin >> temp;
stats.push_back(temp);
}
character *newChar = new character(name, stats);
return newChar;
}
You'll notice in this snippet that I created a pointer function. You can do this, and in fact have to, in order to create a pointer inside of the function and then return the pointer instead of the whole class, which can be unwieldy.
That works. Is there a more efficient way to change the iterator to higher values than just using Objects.begin() followed by it++? It's doable, and I could easily make a function to access a certain point, but why reinvent the wheel (unless it's to make it better ;))?
Thanks, by the way!
EDIT: thanks for the great example. I will definitely be able to learn from it! I've been practically avoiding pointers, mainly because I haven't understood them, but I think I'm starting to figure out a LITTLE bit about them.
That's the best way to iterate that I know of, but if you know specifically where in your vector something is stored, you can just jump right to it. The point of the iterator is that it's independent of any values contained in the list, vector or deque, but it still can point to objects inside that list, so you can move it around freely. With a vector, everything is sequential in memory, which can cause problems with reallocation, but in lists, you don't have that problem, and iterators become even more powerful, because even without the list being stored next to each other in memory, your iterator still knows the memory address of the next item in the list. (It doesn't know the value, just the address, which is why it's a double pointer, not a single.)
Alright. Making my own function to handle it shouldn't be to challenging. I'll also try a list out sometime soon. I won't try it today, though, because I just recently tried out multimaps, maps, and sets for the first time (they're in the program I was having a problem with). They look pretty useful, and I just checked the documentation on them. I'm heading out for now, but thanks again for the help! I've been teaching myself as I go, creating programs as I need them, and it's really nice to know that when I'm really struggling I can find help.
Just as an aside, trailken, if you don't understand pointers, you should probably go back and look in to them very, very thoroughly, because once you start with classes, you're never going to be able to avoid pointers, past the simple life of programs with classes that don't interact.