Стирание элемента из списка

Мне трудно понять, почему код ведет себя так. Прежде всего, я прочитал соответствующий ответный материал и все же нашел объяснения abit. Поэтому мне интересно, может кто-нибудь объяснить это простым способом.

Итак, я удаляю элементы из списка.

Список содержит элементы int, которые являются нечетными и четными числами. Эту часть я понимаю.
Вот код, который я первоначально написал, чтобы удалить нечетные числа из списка

for(list<int>::iterator i = lNo.begin(); i != lNo.end(); i++)
{
if(*i%2 == 0 )
{
lNo.erase(i);
}
else
{
cout << " " << *i;
}
}

С этим кодом программа просто не компилируется, и я прочитал сообщение о том, что программа должна быть закрыта.

Функция стирания работает, когда я пишу этот код:

for(list<int>::iterator i = lNo.begin(); i != lNo.end(); i++)
{
if(*i%2 == 0 )
{
i = lNo.erase(i);
}
else
{
cout << " " << *i;
}
}

Мне просто нужно понять, почему программа работает, когда я кодирую i = lNo.erase (i), а не просто с помощью lNo.erase (i)?

Простой краткий ответ был бы очень признателен.
Я знаю, что разные контейнеры имеют разные ограничения, поэтому какое ограничение я нарушил с помощью оригинальной части кода?

3

Решение

Как указано в документация, erase Функция делает недействительным переданный итератор. Это означает, что его нельзя использовать снова. Цикл не может продолжаться с этим итератором.

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

Обратите внимание, что, поскольку он возвращает итератор для элемента после того, что было стерто, нет необходимости увеличивать это для продвижения, иначе этот элемент не будет проверен на странность. Цикл должен фиксироваться для этого и увеличиваться только тогда, когда стирание не было выполнено.

6

Другие решения

Даже ваш второй код неверен.

Правильный код должен быть таким:

for(list<int>::iterator i = lNo.begin(); i != lNo.end(); /*NOTHING HERE*/ )
{
if(*i%2 == 0 )
{
i = lNo.erase(i);
}
else
{
cout << " " << *i;
++i; //INCREMENT HERE, not in the for loop
}
}

Обратите внимание, что erase() стирает элемент и возвращает итератор к следующему элементу. Это означает, что вам не нужно увеличивать i в вашем коде, когда вы стираете; вместо этого вам просто нужно обновить i с возвращенным значением из erase,

Вы могли бы использовать erase-remove idiom как:

lNo.erase(std::remove_if(lNo.begin(),
lNo.end(),
[](int i) { return i%2 == 0; }),
lNo.end());

Живая демо

5

Дело в том, что вы используете итератор, который не ожидает, что цепочка вашего списка будет изменена.
Поэтому, когда вы вызываете erase () в своем списке, цепочка эффективно изменяется, и ваш итератор больше не действует. Оператор i ++ больше не работает.

Но во 2-й версии вы повторно назначаете свой итератор допустимому объекту, у которого все еще есть нетронутая цепочка, поэтому оператор i ++ может все еще работать.

В некоторых средах у вас есть 2 вида итераторов: тот, который сразу отражает то, что происходит с базовым набором данных (вот что вы используете), и тот тип, который не меняет их цепочку независимо от того, что происходит с базовым набором данных ( так что вам не нужно использовать странный трюк 2-й версии).

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector