-4

I'm creating a program that stores basic data. After inputing data for the first 'person', the program simultaneously loops to the second instance of i without accepting any input. This is hard to explain but I hope someone can help me out. I have a feeling that getline and cin.ignore are causing these problems. I only came across them on this website.

struct info {
     string name;
     string address;
     int phone;
};


int main(){
    int input;
    cout<<"How many people do you want on the list" <<endl;
    cin>>input;
    info arr[input];
    for(int i=0;i<input;i++){
        cout<<"Enter name for person " <<i+1 <<": ";
        getline(cin,arr[i].name);
        cin.ignore(1000,'\n');
        cout<<"Enter the address of " <<arr[i].name <<endl;
        getline(cin,arr[i].address);
        cin.ignore(1000, '\n');
        cout<<"Enter phone number of " <<arr[i].name <<endl;
        cin>>arr[i].phone;
        cout<<endl;
    }
}
mRperFect
  • 3
  • 2
  • `getline` consumes the newline but does not include it in the string, so you don't need to do `cin.ignore`. – user3286380 Feb 10 '14 at 00:22
  • 1
    [Why does `std::getline()` skip the input.](http://stackoverflow.com/questions/21567291/why-does-stdgetline-skip-the-input/21567292#21567292) – 0x499602D2 Feb 10 '14 at 00:24
  • `arr[i].phone;` is meant to do what again please? – πάντα ῥεῖ Feb 10 '14 at 00:24
  • @user3286380 - Deleting cin.ignore messes up the program completely! – mRperFect Feb 10 '14 at 00:30
  • @0x499602D2 - Whoa! I just noticed that.That was a huge error! Thanks a lot. I'll edit it – mRperFect Feb 10 '14 at 00:33
  • Ok so replacing getline with a cin>>arr[i].name solves my problem. But now entering a full name with spaces between first and last names doesn't work. Any ideas? – mRperFect Feb 10 '14 at 00:42
  • @mRperFect `std::getline()` is still the solution in that situation. My answer shows you how to use it appropriately. – 0x499602D2 Feb 10 '14 at 01:04
  • @0x499602D2 - Thanks, your help is immensely appreciated. I'm not(yet) familiar with vectors so understanding this might take a while. Thanks anyway :) – mRperFect Feb 10 '14 at 01:20
  • There's still the option of using C-style arrays, but you'd have to allocate them on the heap (meaning `info* arr = new info[input]`). That's the only way using a runtime variable as the size will work. Don't forget to deallocate that memory when you're finished using `delete[] arr;`. – 0x499602D2 Feb 10 '14 at 01:28
  • @0x499602D2. Can you explain the following line: std::getline(std::cin >> std::ws, arr[i].name); – mRperFect Feb 18 '14 at 22:02
  • @mRperFect I'm guessing it's the `std::cin >> std::ws` that confuses you. `std::ws` is a manipulator like `std::endl` only its job is to discard leading whitespace from the input stream. The expression returns the stream again. It's exactly the same as `std::cin >> std::ws; std::getline(std::cin, arr[i].name)`. – 0x499602D2 Feb 18 '14 at 22:11
  • @mRperFect Well it's not *exactly* the same. "Functionally equivalent" would be the proper term. – 0x499602D2 Feb 18 '14 at 22:23
  • @0x499602D2 Thanks a lot. You are lifesaver man! – mRperFect Feb 18 '14 at 22:30
  • @mRperFect Glad I could help! :) – 0x499602D2 Feb 18 '14 at 22:33
  • Could you also accept my answer below (click the checkmark)? :) – 0x499602D2 Feb 18 '14 at 22:45
  • @0x499602D2 Done! :) So what's the difference between cin>> and std::cin>> ? And how can I highlight my code? – mRperFect Feb 18 '14 at 23:03
  • @mRperFect The difference between `cin` and `std::cin` is that the latter is *fully-qualified*. `std::cin` and `cin` are the same object, but I like to write it as `std::cin` as it is more expressive about where it comes from (it comes from the namespace `std`). Moreover, using `cin` without `std::` is the common thing people do when they use `using namespace std`. [But it is generally looked down upon in the programming community](http://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice) because names in the `std` namespace can clash with other names. – 0x499602D2 Feb 18 '14 at 23:18
  • @mRperFect Also, you highlight your code by wrapping them with the character `\``. – 0x499602D2 Feb 18 '14 at 23:22

1 Answers1

0

Mixing formatted input with unformatted input is always asking for trouble. When you extract input into the stream, a newline is always appended. The newline character is the delimiter for std::getline(), meaning whilst extraction is being performed, if std::getline() finds a newline, it discards that character and stops input.

This is the problem you're running into. After the last formatted extraction cin >> input, the newline is left in the input stream. std::getline() (unlike formatted input functions) do not discard leading whitespace, so it is prone to the disadvantage I explained above.

To solve this issue, you need to discard leading whitespace manually, so the the non-whitespace input is ready to be extracted by the time std::getline() is invoked:

std::getline(std::cin >> std::ws, arr[i].name);
//           ^^^^^^^^^^^^^^^^^^^

std::ws is manipulator which extracts and discards leading whitespace. This should be satisfactory for your purposes.


The following are suggestions that I advise you take. They lead to a substantially cleaner, clearer code structure. And it also reduces a lot of headaches:

1. Your class should have its own inserter/extractor.

You should define your own inserter/extractor to encapsulate I/O semantics for your class. It facilitates a lot of the trouble of manual extraction. Inserters/extractors are typically implemented as friends of the target class so that it may access private data members. Your class in particular has no private data members, but this is still an idiomatic technique nontheless:

struct info
{
    string name;
    string address;
    int phone;

    friend std::ostream& operator<<(std::ostream&, const info&);
    friend std::istream& operator>>(std::istream&,       info&);
};

Inserters/extractors are global functions because they are being defined in terms of (not only) the target class, but also the stream object itself.

2. Use std::vector<T> rather than raw C-style arrays

The following is wrong:

cin >> input;
info arr[input];

This code instantiates an array at compile-time with a size known only at runtime. The possibility of your compiler accepting this code fully depends on how standard-conformant it is. GCC has a non-standard extension which welcomes code like that, but it is non-standard even so and shouldn't be used.

If you need an array of objects whose size is known at runtime, use a vector of those objects. A vector class is already provided for you through the Standard library:

std::cin >> input;
std::vector<info> arr(input);

In fact, the size is superfluous since the vector will grow dynamically as more and more elements are created. You can simply default-construct the vector (unless you have a good reason to specify the size, in which case that many info objects will be default-constructed in the internal buffer of the vector).

std::vector<info> arr;

This is mostly it. Here is an example of your program that applies these two suggestions:

#include <iostream>
#include <vector>

struct info
{
    std::string name;
    std::string address;
    int phone;

    friend std::ostream& operator<<(std::ostream&, const info&);
    friend std::istream& operator>>(std::istream&,       info&);
};

std::ostream& operator<<(std::ostream& os, const info& obj)
{
    return os << "Person's name is: " << obj.name << std::endl
              << obj.name << "'s address is: " << obj.address << std::endl
              << obj.name << "'s phone number is: " << obj.phone << std::endl;
}

std::istream& operator>>(std::istream& is, info& obj)
{
    std::cout << "Enter name for person " << ": ";
    std::getline(is >> std::ws, obj.name);

    std::cout << "Enter the address of " << obj.name << std::endl;
    std::getline(is >> std::ws, obj.address);

    std::cout << "Enter phone number of " << obj.name << std::endl;
    std::cin >> obj.phone;
    std::cout << endl;

    return is;
}

int main()
{
    int input;
    std::cout << "How many people do you want on the list" << std::endl;
    std::cin >> input; // just as a formality, but really the user can enter
                       // as much as they want.
                       // The value of input will be ignored.

    std::vector<info> arr;
    info obj;

    while (std::cin >> obj)
    {
        arr.push_back(obj);
    }

    for (const auto& item : arr)
        std::cout << item << std::endl;
}

If you have any questions, please leave them as a comment and I will try to help you as best as possible.

0x499602D2
  • 87,005
  • 36
  • 149
  • 233