Список Iterator Remove()

У меня есть итератор списка, который проходит через список и удаляет все четные числа. Я могу использовать итератор списка, чтобы распечатать цифры в порядке, но я не могу использовать список remove() и передать в разыменованный итератор.

Я заметил, что когда действие remove() действует, * itr повреждается? Может кто-нибудь объяснить это?

#include <iostream>
#include <list>

#define MAX 100

using namespace std;

int main()
{
    list<int> listA;
    list<int>::iterator itr;

    //create list of 0 to 100
    for(int i=0; i<=MAX; i++)
        listA.push_back(i);

    //remove even numbers
    for(itr = listA.begin(); itr != listA.end(); ++itr)
    {
        if ( *itr % 2 == 0 )
        {
            cout << *itr << endl;
            listA.remove(*itr);    //comment this line out and it will print properly
        }
    }
}

Ответ 1

Есть несколько проблем с вашим кодом выше. Во-первых, remove приведет к аннулированию любых итераторов, указывающих на удаленные элементы. Затем вы продолжаете использовать итератор. Трудно сказать, какой элемент remove будет стираться в общем случае (хотя и не в вашем), поскольку он может удалить более одного.

Во-вторых, вы, вероятно, используете неправильный метод. Удалить будет проходить через все элементы в списке, ища какие-либо соответствующие элементы - это будет неэффективно в вашем случае, потому что есть только один. Похоже, вы должны использовать метод erase, вы, вероятно, хотите только стереть элемент в позиции итератора. Хорошая вещь о erase заключается в том, что он возвращает итератор, который находится в следующей допустимой позиции. Идиоматический способ его использования выглядит примерно так:

//remove even numbers
for(itr = listA.begin(); itr != listA.end();)
{
    if ( *itr % 2 == 0 )
    {
        cout << *itr << endl;
        itr=listA.erase(itr);
    }
    else
      ++itr;
}

Наконец, вы также можете использовать remove_if для выполнения того же действия, что и вы:

bool even(int i) { return i % 2 == 0; }

listA.remove_if(even);

Ответ 2

Вы не можете использовать итератор после удаления упомянутого элемента.

Однако список итераторов, которые ссылаются на не удаленные элементы после remove(), должен оставаться в силе.

Ответ 3

Можно ли использовать что-то вроде этого:

container.erase(it++);

Я попытался в этом примере:

int main(){

 list<int>*a=new list<int>;
 a->push_back(1);
 a->push_back(2);
 a->push_back(3);

 list<int>::iterator I;

 I=a->begin(); ++I;

 a->erase(I++);
 cout<<*I<<endl;
}

и он отобразил 3, как я и хотел. Теперь я не знаю, действительно ли это или один из тех, которые "иногда работают, а иногда нет".

EDIT: Возможно, это из-за компилятора. Например, компилятор, который я использую (GNU gcc-g++), обрабатывает списки (std::) как циклические, т.е. Если я увеличиваю итератор после list- > end(), он помещает вас в начало.

Ответ 4

Поскольку итераторы зависят от длины оставшейся структуры, большинство итераторов не позволяют изменять список, пока используется итератор. Если вы хотите пройти и изменить список, вам придется использовать цикл, не зависимый от итератора.