C ++ remove_if перезаписывает мой вектор - PullRequest
1 голос
/ 21 января 2012

Кажется, что мой remove_if перезаписывает элементы, которые не отфильтрованы, значениями отфильтрованных элементов. Цель этого кода - позволить пользователю фильтровать и отображать только учителя из определенной категории. (Не удаляя ни одного элемента) Вот некоторые из кода

static string compare;
static string debug;

bool filter_Cat (Teacher &t) 
{ 
    return (t.getCat() != compare); 
}

void filterCat (vector<Teacher> &t)
{
   vector<Teacher>::iterator i;
   vector<Teacher>::iterator newedited = remove_if(t.begin(), t.end(), filter_Cat);
   for (i = t.begin(); i != newedited; ++i)
   {
     Teacher& te = *i;
     te.getName();
     cout << "\t";
     te.getCategory();
     cout << "\t";
     te.getLocation();
   }
 }

 void filterTutorCat(vector<Teacher> &t)
 {
    int choice;
    cout << "No\tCategory" << endl
         << "1\tEnglish" << endl
         << "2\tMath" << endl
         << "3\tScience" << endl
         << "Choose the category you wish to filter :";
    cin >> choice;
    getline(cin, debug);

    if(choice <= 3 && choice > 0)
    {
        if (choice == 1)
        {
          compare = "English";
          filterCat(t);
        }
        if (choice == 2)
        {
          compare = "Math";
          filterCat(t);
        }
        if (choice == 3)
        {
          compare = "Science";
          filterCat(t);
        }

    }
    else
    {
        cout << "Invalid Option" << endl;
    }
 }

Ответы [ 2 ]

2 голосов
/ 21 января 2012

remove_if смещает элементы, для которых функция сравнения возвращает false, справа налево; другими словами, он перезаписывает элементы, для которых сравнение возвращает true, с элементами, для которых сравнение возвращает false. Однако размер вектора не меняется.

Это читается как ,

Удаляет все элементы, удовлетворяющие определенным критериям, из диапазона [first, last). Первая версия удаляет все элементы, равные значению, вторая версия удаляет все элементы, для которых предикат p возвращает true.

Удаление выполняется путем смещения элементов в диапазоне таким образом, что стираемые элементы перезаписываются. Элементы между старым и новым концами диапазона имеют неопределенные значения. Итератор до нового конца диапазона возвращается. Относительный порядок оставшихся элементов сохраняется.

То, что вы хотите сделать, должно быть выражено как:

void filterCat (vector<Teacher> &v)
{
   for (vector<Teacher>::iterator it = v.begin(); it != v.end() ; ++it)
   {
      if (!filter_Cat(*i))
      {
           std::cout << i->getName() <<"\t" << i->getCategory() << std::endl;
      }
   }
 }

Кажется, в вашем коде getName() печатает имя, которое в идеале не должно делать, вместо этого оно должно возвращать имя. Поэтому я бы предложил вам изменить его, чтобы оно возвращало имя. И сделать то же самое для getCategory. Выберите свое имя правильно. Если это getName(), вы должны получить ваше имя, вернув его; если это printName(), тогда оно должно напечатать имя.


Кроме того, код, который вы написали, не очень хорош:

  • Вам следует избегать глобальных переменных.
  • Вам следует избегать если-еще как можно больше. Узнайте лучшие способы.
  • Вы должны узнать о функциональных объектах (или функторе)
  • Вы должны узнать о const функциях-членах.
  • Вы должны понимать разницу между iterator и const_iterator и их использованием.
  • Вы должны понимать разницу между константной ссылкой и неконстантной ссылкой. И попробуйте использовать их соответствующим образом.

Так что я бы написал ваш код как:

//this is functor, not a function
struct filter_cat
{
   std::string m_cat; //use member data, avoid global variable
   filter_cat(std::string const & cat) : m_cat(cat) {}
   bool operator()(Teacher const & t) const  //const member function
   { 
     return (t.getCat() != m_cat); //getCat should be const member function
   }
};

//pass vector by const reference
void filterCat (vector<Teacher> const & v, filter_cat filter)
{
   //use const_iterator here, instead of iterator 
   for (vector<Teacher>::const_iterator it = v.begin(); it != v.end() ; ++it)
   {
      if (!filter(*i))
      {
           //getName and getCategory should be const member function
           std::cout << i->getName() <<"\t" << i->getCategory() << std::endl;
      }
   }
}

void filterTutorCat(vector<Teacher> const &t)
{
    int choice;
    cout << "No\tCategory" << endl
         << "1\tEnglish" << endl
         << "2\tMath" << endl
         << "3\tScience" << endl
         << "Choose the category you wish to filter :";
    cin >> choice;
    getline(cin, debug);

    //avoid if-else as much as possible, learn better ways!
    std::string cats[] = {"English", "Math", "Science"};

    if(choice <= 3 && choice > 0)
    {
          filterCat(v, filter_cat(cats[choice-1]));
    }
    else
    {
        cout << "Invalid Option" << endl;
    }
}

Как отмечено в комментариях: getCat, getName и getCategory должны быть постоянными функциями-членами. Фактически, если getCategory возвращает категорию, то getCat даже не требуется.

Решил мою проблему.

1 голос
/ 21 января 2012

remove_if собирает значения, для которых filter_Cat возвращает false в начале контейнера. Хотя это не уменьшает количество элементов в контейнере, оно также не дает никаких гарантий относительно значений элементов за пределами возвращаемого диапазона. Таким образом, вы теряете значения при использовании remove_if.

...