I have a question on Chapter 4, exercise 1 of Michael Dawson's "Beginning C++ through Game Programming 2nd Edition".

The chapter goes over STL, vectors and iterators. The assignment is to create a program that allows the user to edit games in a list. In the book we learn about member functions .begin, .end, .insert, .erase, etc etc. The thing is I don't want to ONLY allow the user to point to the beginning OR end of the vector object "gameLibrary". Here's what I thought would work:

Create an int variable that allows the user to enter the number in the list they want to select, THEN with that int number, use it to refer to a number in the vector object container "gameLibrary". Ideally then use that number to a).erase the entry, b).insert new entry.

Here's the problems I'm having:
1 - FOR LOOP
This doesn't work:

for (int i = 0; i < gameLibrary.size(); ++i)
        counter = i;
        cout << counter << "-";
        cout << gameLibrary[i] << endl;

but this does:

for (int i = 0; i < gameLibrary.size(); ++i)
        cout << gameLibrary[i] << endl;

Is the use of the 'i' throwing off my counter? I keep getting an error that 'i' changed in the first example. Is the for loop automatically counting up? or is the use of a for loop not intended to use the 'i' more than once per cycle in these cases?


2 - Deferencing Iterator
Iterators established earlier:
vector<string>::iterator myIterator;
vector<string>::const_iterator iter;

This works:

cout << "\nThe item name '" << *myIterator << "' has ";
    cout << myIterator->size() << " letters in it.\n";

This gives me an error:

cout << "\nThe item name '" << myIterator << "' has ";
    cout << myIterator.size() << " letters in it.\n";

I don't understand why the deferencing iterator is ok, but when I try to cout the non deferenced iterator it gives me an error? I would think the non-Def iterator would just cout the number of the container it's referring to.


THANKS in advance to anyone helping.

#1: The use of variable i is not the problem. The problem is that you failed to use { and } to surround multi-line if statement

for (int i = 0; i < gameLibrary.size(); ++i)
{
        counter = i;
        cout << counter << "-";
        cout << gameLibrary[i] << endl;
}

Ha, did I earn my noob medal yet? Thanks for the help.

No idea why #2 is happening do you?

Also I'm running into this error. I want the user to enter a 'selection' int, then I use that to refer to which element in a string vector they are referring to. Any idea why the following wont work?

3 - VECTOR ARGUMENT ERROR

cout << "\n\nSelect a Game by number: \n";
     int selection;
     cin >> selection;
     
     if(selection < gameLibrary.end())   // THIS LINE ERROR!!!!
     { 
     cout << "\n You have selected: " << gameLibrary[(selection - 1)];  
     
     //Enter New Game Title   
     string newGame;
     cout << "\nEnter new game title: ";
     cin >> newGame;

ENTIRE CODE (SO FAR) FOR REFERENCE:

#include <iostream>
#include <string>
#include <vector>

using namespace std;

bool bswitch = true;

int main()
{
    //Initializing library
    vector<string> gameLibrary;
    gameLibrary.push_back("mario");
    gameLibrary.push_back("zelda");
    gameLibrary.push_back("street fighter 4");
    
    vector<string>::iterator iterEdit;
    vector<string>::const_iterator iter;
    
    //Display # of Games
    cout << "You have " << gameLibrary.size() << " games.\n";
    int counter;

do
{   
    //MAIN MENU
    //Display Library
    cout << "\nYour Games:\n";
    for (iter = gameLibrary.begin(); iter != gameLibrary.end(); ++iter)
        cout << *iter << endl;


     cout << "\n\nSelect a Game by number: \n";
     int selection;
     cin >> selection;
     
     if(selection < gameLibrary.end())
     { 
     cout << "\n You have selected: " << gameLibrary[(selection - 1)];  
     
     //Enter New Game Title   
     string newGame;
     cout << "\nEnter new game title: ";
     cin >> newGame;
     
     //iterEdit = gameLibrary
     iterEdit = gameLibrary.insert((gameLibrary.begin() + selection), newGame);
     gameLibrary.erase((gameLibrary.begin() + (selection - 1)));
     //*iterEdit = newGame;
     }
     else
     {
         cout << "invalid selection";
     }
}while(bswitch == true); 
     
system("PAUSE");
return 0; 
}

Edited 6 Years Ago by crapgarden: n/a

For #2, iterators are just classes. They have certain operation defined for them.
For example they define the deference operator, which is why you can deference them.
Similarly, the library does not define the operator << for the iterators, thus it is
a compiler error.

For #3, "selection < gameLibrary.end())"

.end() function returns an iterator to the one-pass-the-end element. It is not what
you want there. What you want is the .size() function.

firstPerson is right. Iterators are designed to work like array pointers.
So, in C, you would say:

// this is standard C usage
int a[10]; // the array we want to use
int* begin = a;

// end-pointer, to test when our pointer has gone too far
int* end= a+10; // must NEVER actually try to use this pointer
// 'end' points to hellish unknown memory beyond the array

int* iter = begin;
while( iter != end ) // use 'end' only to test when we have gone too far
  {
   *iter = 42; // use the pointer to write the array
   ++iter; // count the pointer forward
  }

.. becomes, for C++ STL :

// this is standard C++ usage
#include <vector>
using namespace std;

vector<int> a(10); // the array we want to use

vector<int>::iterator iter = a.begin();
while(  iter != a.end() ) // use 'end' only to test
  {
   *iter = 42; // use the iterator to write the array
   ++iter; // count the iterator forward
  }

Wow, super helpful - thanks!!

Here's the code I got working. Any idea why when I enter a title that has a space in it (ex: zelda 2) it drops anything after the space? I'm using strings right?

//gameLibrary v3

#include <iostream>
#include <string>
#include <vector>

using namespace std;

bool bswitch = true;

int main()
{
    //Initializing library
    vector<string> gameLibrary;
    gameLibrary.push_back("mario");
    gameLibrary.push_back("zelda");
    gameLibrary.push_back("street fighter 4");
    
    vector<string>::iterator iterEdit;
    vector<string>::const_iterator iter;
    
    //Display # of Games
    cout << "You have " << gameLibrary.size() << " games.\n";
    int counter;

do
{   
    //MAIN MENU
    //Display Library
    cout << "\nYour Games:\n";
    for (iter = gameLibrary.begin(); iter != gameLibrary.end(); ++iter)
        cout << *iter << endl;
    

     cout << "\n\nSelect a Game by number: \n";
     int selection;
     cin >> selection;
     
    // HELP http://www.daniweb.com/forums/post1269552.html#post1269552
     if(selection <= gameLibrary.size())
     { 
     cout << "\n You have selected: " << gameLibrary[(selection - 1)];  
     
     //Enter New Game Title   
     string newGame;
     cout << "\nEnter new game title: ";
     cin >> newGame;
     
     //iterEdit = gameLibrary
     iterEdit = gameLibrary.insert((gameLibrary.begin() + selection), newGame);
     gameLibrary.erase((gameLibrary.begin() + (selection - 1)));
     //*iterEdit = newGame;
     }
     else
     {
         cout << "invalid selection";
     }
}while(bswitch == true); 
     
system("PAUSE");
return 0; 
}

Also I'm trying to make an alternate version that will let me choose items in the menu by number. The first problem I have here is that I try to use iter to refer to the vector container number in the vector "gameLib", then a dash, then defer to the actual iter value IN the container to output what's inside.

I get an error for this part:

//list library
cout << "\nYour Games: \n";
for (iter=gameLib.begin(); iter < gameLib.end(); ++iter)
    cout << iter << "-" << *iter << endl;

Above, first person explained:

For #2, iterators are just classes. They have certain operation defined for them.
For example they define the deference operator, which is why you can deference them.
Similarly, the library does not define the operator << for the iterators, thus it is
a compiler error.

Any idea how I should get this to work if the << wont work with iter. I just want to output which number in the container it's referring to based on actual data FROM that container, not some shoddy workaround I would probably come up with. Maybe I'm jumping ahead of myself but this seems basic enough. Solving whatever I'm doing wrong here will likely allow me to ALSO then refer to the items by number.

Thanks again for the help!

ALL CODE FOR REF

//game Library Manager

#include <iostream>
#include <vector>
#include <string>
#include <cstdlib>

using namespace std;

bool bMenuSwitch = true;

int main()
{
//setup library
vector<string> gameLib(10, "empty slot");
string userName;
string userLibrary;

vector<string>::const_iterator iter;
vector<string>::iterator iterEdit;

cout << "Please enter your name: ";
cin >> userName;

cout << "\n\t\tWelcome to " << userName << "'s Game Library";   
    
//MAIN MENU loop
do
{
//list library
cout << "\nYour Games: \n";
for (iter=gameLib.begin(); iter < gameLib.end(); ++iter)
    cout  << "-" << *iter << endl;

//list options
char menuSelection;
cout << "\nWhat would you like to do?\n";
cout << "\nEnter 'c' to create an entry. Enter 'd' to delete an entry.\n" << endl;
cin >> menuSelection;

  if(menuSelection == 'c')
  {
       //CREATE entry
        string newTitle;
        cout << "/nPlease enter your new title: " << endl;
        cin >> newTitle;
        gameLib.insert(gameLib.begin(), newTitle);
        }
        
   if(menuSelection == 'd')
    {   //DELETE entry
       cout << "\nYou chose delete\n";
       iterEdit = gameLib.begin();
       gameLib.erase((iterEdit));
       }

   if(menuSelection != 'd' || 'c')
       {
       cout << "INVALID ENTRY";
       }

        
}while(bMenuSwitch==true);   
    
    
    system("PAUSE");
    return 0;
}

Edited 6 Years Ago by crapgarden: n/a

Also, use getline(cin,stringVariable) to accept a whole line of input instead
of just cin >> stringVariable because cin stop reading input after
it sees a whitespace.

Computer memory is numbered. Every slot can hold one byte. An address is the count of that particular storage place.
And, iterators are like pointers, that is, addresses in computer memory.

int i=32; // the value of 'i' is 32, but where is that '32' stored?
int* ptr= &i; // 'ptr' holds the address of 'i'
cout<< ptr<< endl; // .. some big number like 0012FF24.  A spot in your computer's memory

It usually does no good to show where the item is located in RAM.
If you want the ordinal, that is the position, in order, of the element in memory,
you can subtract one pointer from another:

int arr[10]= {10,20,30,40,50,60,70,80,90,100};
int * begin = &arr[0]; // the address of the first element
int * end = &arr[10]; // the address of the 11th item (don't try to use!)
cout<< "the offset of item 11 is: "<< end-begin << endl;

No matter how many bytes each item requires, when you subtract two pointers, the C/C++ compiler converts the result into numbers of that type. Good old C pointer arithmetic.
.. And all this is simpler if we remember that an array in C/C++ is just a pointer to the first item. You actually never need to define a pointer-to-begin:

int ar[10]= {10,20,30,40,50,60,70,80,90,100};
// int * ptr1 = &ar[0]; // the address of the first item, but who needs it? 
int * ptr2 = &ar[9]; // the address of the 10th element
cout<< "the offset of element 9 is: "<< ptr2-ar << endl; // notice, 'ptr2-ar'

.. because the array declaration 'ar' is defined as a pointer to the zeroth element ..

int Arr[10]= {10,20,30,40,50,60,70,80,90,100};
cout<< "The array 'arr' itself : "<< Arr<< endl;
int * Ptr1 = &Arr[0]; // the address of the first element in 'arr'...
cout << "address of first element IN array 'arr' :" << Ptr1<< endl;
cout<< "In other words, they're " << (Ptr1==Arr ?"equal!":"totally different") << endl;

So, you might have guessed, you can do the same with iterators.

vector<int> vArr; // an array
	for(int i=0; i<10; ++i) vArr.insert(vArr.end(),i*5); // fill it
	// step through, using iterators
	for( vector<int>::iterator iter= vArr.begin(); iter !=vArr.end(); ++iter )
		cout<< iter - vArr.begin() << " --> " << *iter << endl;

No matter how many bytes each item requires, when you subtract two pointers, the C/C++ compiler converts the result into numbers of that type. Good old C pointer arithmetic.

What if string is the type? It converts it to strings?

Strings are slightly different in C, because they are not a real separate type. A string in C is defined as an array of plain chars, with a zero for the last character.

char string[] =
	{'I',' ','a','m',' ','a',' ','C',' ','s','t','r','i','n','g', 0 };

int main(int argc, char* argv[])
{
	printf( string ); // this works fine.
	return 0;
}

But, remember about arrays being just pointers (from earlier post) ?
What people call a string, in C, is just a pointer to the first character in an array, where the last character is a zero. If there is no zero at the end, it is not a C string.

#include "stdafx.h"
char* aString = "123";  // this is the same as saying "char aString[4] = {'1','2','3',0};
int main()
	{
	// print the string, the hard way...
	char* pChar;  // a pointer to a character...
	pChar = aString;  // points to the first character in an array of chars.
	while( *pChar != 0 ) // quit when the char is zero
		{
		putchar(*pChar); // print one character
		++ pChar; // move the pointer to the next character
		}
	}

So, a C string is a pointer to a character.
And, you can definitely have an array of those!

// array of strings, which is just an array of pointers
char * array_of_chars[] = { "1", "2", "3" }; // this is an array of pointers

// which is the same as:

// three strings, i.e. arrays of characters ending in a zero-terminator
char element0[] = { '1', 0 };
char element1[] = { '2', 0 };
char element2[] = { '3', 0 };

// Another array of pointers.
// Each element in this array points to the first character of a string.
char * another_array_of_chars[] = { element0, element1, element2 };

Arrays of whole strings are not required to end in a zero-pointer.
Some programmers like to do it that way.
Others prefer to just use a separate counter, telling the user how many strings are in the array...

// the parameters to every C program are: an array of strings, and a counter telling the 
// user how many strings are in that array:
int main( int argc, char * argv[] )
	{
	for( int i=0; i< argc; ++i )
		puts( argv[i] );
	}

.. And, pointer arithmetic works on pointers to strings. So you could walk through a string array this way:

char * zero_terminated_string_array[] = 
	// notice, a zero-terminator on the end:
	{ "stringZero", "stringOne", "stringTwo", 0 };
int main()
	{
	for( char ** i = zero_terminated_string_array; *i != 0; ++i )
		puts( *i );
	}

STL strings are done the same way exactly. They are arrays of characters.
String collections, like vector<string> are like arrays of strings.
Iterators to a string collection also work the same. Pointers to arrays of characters.

#include <string>
#include <vector>
using namespace std;
int main()
	{
	// a C string is just an array of characters.
	string a= "123";
	// step through the array one at a time
	for( string::iterator i= a.begin(); i!= a.end(); ++i )
		putchar( *i );

	// a collection (vector) of strings, is like an array of char pointers..
	vector<string> v;
	v.insert(v.end(), "first");
	v.insert(v.end(), "second");
	v.insert(v.end(), "third");

	// step through an STL-style array of strings
	for( vector<string>::iterator i= v.begin(); i!= v.end(); ++i )
		puts( i->c_str() );

	// iterator arithmetic works like pointer arithmetic: 
	vector<string>::iterator begin= v.begin(); // first element
	vector<string>::iterator end= v.end(); // one past the last element

	// iterators can be subtracted
	printf( "the number of strings in v is: %d\n",
		// difference between two iterators, in a vector of strings
		end-begin
		);
	}

Aaaah, so strings are new to C++ from C. In C you used to have to create a string as an array of char types.

When you subtract a string from another string, or attempt to access its elements, it likes to break them back down to c style strings first.

I realize the analogy is a bit shaky but:
Kind of like vector images being turned to bitmaps for editing in photoshop, then back into a vector graphic. Or a nurbs > Poly for editing > back to nurbs.

Aaaah, so strings are new to C++ from C. In C you used to have to create a string as an array of char types.

This part of your summary is correct! But...

When you subtract a string from another string, or attempt to access its elements, it likes to break them back down to c style strings first.

I realize the analogy is a bit shaky but:
Kind of like vector images being turned to bitmaps for editing in photoshop, then back into a vector graphic. Or a nurbs > Poly for editing > back to nurbs.

That part of your comment is not right. You can never subtract one std::string from another one. It is iterators you can subtract, because iterators work like pointers. And, like pointers, iterators do not touch the data. There is no conversion, no bit manipulation. An iterator just points to something inside a container. It is an address. It's incredibly fast.

int main(int argc, char* argv[])
{
	std::string x = "some stl string";
	std::string::iterator begin = x.begin();  // this is an iterator
	std::string::iterator end = x.end(); // this is another iterator
	int LEN = end-begin;  // iterators within the same container can be subtracted
	// try subtracting two strings. this will confuse the compiler plenty
	std::string y = "another stl string";
	void * this_will_never_work = x - y ; // subtract two strings?  nope.
	return 0;
}

ha, yeah that's pretty stupid. How can you subtract "Cow" from "Toyota Hybrid"?
I get it now, the iterators can work mathematically because the containers they refer to can be used as mathematical values....right? Closer?

ha, yeah that's pretty stupid. How can you subtract "Cow" from "Toyota Hybrid"?
I get it now, the iterators can work mathematically because the containers they refer to can be used as mathematical values....right? Closer?

You can, depending on the context. For example, in set theory you can subtract sets.
Let A = {a,b,c,d} and B = {a,d,x,y}, then A - B = {c,d}.

Okay, sorry I'm feeling like I'm getting mixed answers now:

Wisaacs:

You can never subtract one std::string from another one. It is iterators you can subtract, because iterators work like pointers. And, like pointers, iterators do not touch the data. There is no conversion, no bit manipulation. An iterator just points to something inside a container. It is an address. It's incredibly fast.

FirstPerson

You can, depending on the context. For example, in set theory you can subtract sets.
Let A = {a,b,c,d} and B = {a,d,x,y}, then A - B = {c,d}.

So you CAN or CANNOT subtract a string from a string?

@FirstPerson
How does that calculation end up equaling {c,d}?
It seems like you would end up with {b,c}, or {} nothing.

Okay, sorry I'm feeling like I'm getting mixed answers now:

Wisaacs:


FirstPerson


So you CAN or CANNOT subtract a string from a string?

@FirstPerson
How does that calculation end up equaling {c,d}?
It seems like you would end up with {b,c}, or {} nothing.

Opps, your right, the result would be {b,c}.

You cannot subtract strings because it doesen't make sense. But I'm just telling
you that you can subtract sets, which strings could represent, and by defining the appropriate operator.

Edited 6 Years Ago by firstPerson: n/a

This article has been dead for over six months. Start a new discussion instead.