voq
voq

Reputation: 73

Why does console want me to enter something after I use cin.ignore?

I have an array and I need to remove one element from it by its name. When I'm entering a name of magazine I want to remove, an input line is called by console, and it doesn't matter what I'll put in there, I can even press an instant enter.

int removeMagazine(string name[], double price[], int length)
{
    if (length > 0)
    {
        length--;
        string removeName;
        int removeIndex;

        cout << "Enter a name of the magazine you want to remove." << endl;
        getline(cin, removeName);
        removeIndex = 0;
        for (int index = 0; index < length; index++)
        {
            if (removeName == name[index])
                removeIndex = index;
        }

        for (removeIndex; removeIndex < length; removeIndex++)
        {
            name[removeIndex] = name[removeIndex + 1];
            price[removeIndex] = price[removeIndex + 1];    
        }
        cin.clear();
        cin.ignore();
    }
    else
    {
        cout << "Could not remove from array. Array is empty!\n";
    }
    
    return length;
}

Upvotes: 0

Views: 198

Answers (2)

sweenish
sweenish

Reputation: 5202

An alternative method that demonstrates using only "standard commands." As noted, commands isn't correct vocabulary, and everything in the code below is part of Standard C++.

#include <algorithm>
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>

// A struct allows you to represent a magazine with a single type
// It can hold all the necessary properties
struct Magazine {
  std::string name;
  double price = 0.00;

  Magazine(std::string n, double p) : name(n), price(p) {}
};

// These two function are for demonstration purposes (gives us our output)
std::ostream& operator<<(std::ostream& sout, const Magazine& mag) {
  return sout << std::setw(12) << std::left << mag.name << ": $" << std::fixed
              << std::showpoint << std::setprecision(2) << mag.price;
}

template <typename Container>
void print(const Container& container) {
  for (const auto& i : container) {
    std::cout << i << '\n';
  }
}
// End of extra stuff for demonstration purposes only

int main() {
  // Vectors are arrays that can change their size automatically.
  // Because we created a Magazine type (with our struct), we only need one
  // vector
  std::vector<Magazine> shelf{
      {"New Yorker", 5.99}, {"Vanity Fair", 5.99}, {"GQ", 5.99}};
  print(shelf);
  std::cout << "\n\n";

  std::string toFind("Vanity Fair");  // Handle 'what' separate from erasing

  // std::vector has an erase() function built in
  // This 'one-liner' replaces your function
  shelf.erase(std::find_if(shelf.begin(), shelf.end(),
                           [&toFind](auto mag) { return mag.name == toFind; }));

  print(shelf);
}

Output:

New Yorker  : $5.99
Vanity Fair : $5.99
GQ          : $5.99


New Yorker  : $5.99
GQ          : $5.99

The most convenient change is the use of a struct to create a new type called Magazine. It holds both the name and the price. This cuts your amount of work in half, since you'd need only an array of Magazines and not two arrays that you have to always manage together.

The next major convenience is the use std::vector over a C-array. std::vector can be treated like an array, but it is far more versatile. In the code, we take advantage of the built-in .erase() function in conjunction with std::find_if().

One other recommendation for your code is to separate your concerns better. Your function to erase should only worry about erasing. It should not also be asking for what to erase. That is a separate task, and should be handled elsewhere.

I debated using std::put_money when printing the price, but that is a whole other can of worms.

This code is likely very different from what you've seen so far. C++ is a big language, and there's a lot to learn about it. I believe that the answer provided by rustyx will suit your needs better, but your comment about being allowed to use "standard commands" was my main motivation for posting this alternative method.

Upvotes: 0

rustyx
rustyx

Reputation: 85541

You don't need cin.ignore() after getline().

Unlike operator >>, getline() extracts also the delimeter character, leaving nothing in the stream.

cin.ignore() reads and ignores exactly 1 character from cin. So if called after getline(), cin.ignore() will block until another character is entered.

Upvotes: 1

Related Questions