We can work with a smaller example. (It is always good to reduce your problem to the smallest example you can manage while still producing the problem.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
#include <iostream>
#include <string>
int main()
{
std::string name ;
int fuelPods ;
for ( unsigned i=0; i<3; ++i )
{
std::cout << "Enter name: " ;
std::cin >> name ;
std::cout << "Fuel pods: " ;
std::cin >> fuelPods ;
}
}
|
Name
> Harvey Wallbanger
Fuel pods
>
Name
>
Fuel pods
>
Name
>
Fuel pods
> Press any key to | |
So, what happens? When the user is asked to enter a name and enters "Harvey Wallbanger\n", the subsequent
cin >> name
only extracts the first word, which means that " Wallbanger\n" is still present in the stream when we get to the next extraction operation, which expects a number as input. Obviously what is already in the input stream cannot be stuffed into an int, so the stream goes into an error state. That state is never cleared, so all subsequent extraction operations fail.
Typically when you want to extract a full line of input, spaces and all, you would use
std::getline. It extracts a line of text, all whitespace preserved, up to and including the next newline in the input stream. That newline is discarded by the function.
http://www.cplusplus.com/reference/string/string/getline/
So, we might expect the following to work:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
#include <iostream>
#include <string>
int main()
{
std::string name ;
int fuelPods ;
for ( unsigned i=0; i<3; ++i )
{
std::cout << "\nName\n> " ;
std::getline(std::cin, name) ;
std::cout << "\nFuel pods\n> " ;
std::cin >> fuelPods ;
}
}
|
Name
> Harvey Wallbanger
Fuel pods
> 30
Name
>
Fuel pods
> 40
Name
>
Fuel pods
> 10 | |
But, as you can see, we only get the opportunity to enter one name. What is this, you're asking? Well, the key is in understanding how the formatted extraction works, and how it interacts with unformatted extraction (such as
getline.)
When we did
std::cin >> name
and entered
"Harvey Wallbanger\n" we saw that what was left in the input stream after extraction was
" Wallbanger\n". Note that the whitespace after Harvey was left in tact.
When we do
std::cin >> fuelPods
and enter a number like so:
"30\n" what do you think is left in the stream after our extraction?
"\n" is. So, when we get back to the top of the loop, our next extraction operation (
getline) encounters a newline in the input stream right away. And since it only extracts input up to the first newline it encounters, the string it extracts is empty.
Not quite what we wanted. Fortunately, now that we know what the problem is we can just ignore a character after the last formatted extraction operation that comes before an unformatted extraction operation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
#include <iostream>
#include <string>
int main()
{
std::string name ;
int fuelPods ;
for ( unsigned i=0; i<3; ++i )
{
std::cout << "\nName\n> " ;
std::getline(std::cin, name) ;
std::cout << "\nFuel pods\n> " ;
std::cin >> fuelPods ;
std::cin.ignore() ;
}
}
|
Name
> Harvey Wallbanger
Fuel pods
> 10
Name
> Mellifluer Cadbury
Fuel pods
> 5
Name
> Cassidy Newberg III
Fuel pods
> 3 | |
As you can see, that works. At least as long as the user enters what we expect. Keep in mind that this implementation is still fairly fragile, but it should clue you into what was going on here.