«он» по-прежнему указывает на то же место, стирание не обновляет итератор само по себе, вы должны сделать это путем сброса итератора. В действительности, «это» указывает на старое местоположение, которое было стерто из вектора, но все еще содержит старые данные.

// erasing from map
#include <iostream>
#include <map>
using namespace std;

int main ()
{
  map<char,int> mymap;
  map<char,int>::iterator it(mymap.begin());

  // insert some values:
  mymap['a']=10;
  mymap['b']=20;
  mymap['c']=30;
  mymap['d']=40;
  mymap['e']=50;
  mymap['f']=60;

  it=mymap.find('a');
  mymap.erase (it);                   // erasing by iterator

  // show content:
  for (; it != mymap.end(); it++ )
    cout << (*it).first << " => " << (*it).second << endl;
  return 0;
}

у это дает вывод, как

a => 10
b => 20
c => 30
d => 40
e => 50
f => 60

не должен"a => 10" удалить в любом случае, но если я объявлюit = mymap.begin() в цикле все идеально. Почему?

Программа адаптирована из:http://www.cplusplus.com/reference/stl/map/erase/

 karlphillip12 янв. 2011 г., 17:03

Ответы на вопрос(5)

Это связано с тем, какmap реализовано. Допустим, это какое-то дерево, например:

class map_node {
    char key;
    int  value;
    map_node* next;
    ...
};

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

Решение Вопроса

map делает недействительными итераторы, указывающие на этот элемент (после того, как этот элемент был удален). Вы не должны повторно использовать этот итератор.

Начиная с C ++ 11erase() возвращает новый итератор, указывающий на следующий элемент, который можно использовать для продолжения итерации:

it = mymap.begin();
while (it != mymap.end()) {
   if (something)
      it = mymap.erase(it);
   else
      it++;
}

До C ++ 11 вам пришлось бы вручную перемещать итератор к следующему элементу до того, как произойдет удаление, например, так:

mymap.erase(it++);

Это работает, потому что побочный эффект постинкрементаit++ происходит раньшеerase() удаляет элемент Поскольку это может быть не сразу очевидно, предпочтительным вариантом C ++ 11 выше.

 Notinlist15 мая 2012 г., 16:03
Не работает с G ++:codepad.org/D2lApTLL , Проблема в том, чтоerase() метод был первоначально в 1998 году определен для возвращенияvoid, Afaik C ++ 03 изменил это, но все еще не поддерживается g ++.
 sth07 июн. 2012 г., 00:34
@OlivierD: Это удалит не текущий элемент, а следующий элемент. И тогда он оставит итератор, указывающий на этот удаленный элемент, поэтому у него будет та же проблема, что и у кода в вопросе.
 karlphillip12 янв. 2011 г., 17:02
 OlivierD06 июн. 2012 г., 21:45
Не должен ли ваш первый фрагмент кода прочитатьmymap.erase(++it) (предварительное увеличение) вместо этого как нет-нет?
 OlivierD07 июн. 2012 г., 19:32
Я смущен предложением. Вы говорите: «Вы не должны повторно использовать этот итератор (Хорошо) или переместите итератор к следующему элементу до того, как будет выполнено удаление (Преинкремент), например, вот так », а затем представьте пример с постинкрементом, который затем повторяется как пример хорошего кода. Я бы ожидал пример плохого кода после этого предложения.

erase() делает недействительным итератор. В этом случае происходит то, что итератор указывает на остаточное значение, оставленное в памяти (но не полагайтесь на это неопределенное поведение!). Сбросьте итератор с помощьюit=mymap.begin() перед циклом для желаемых результатов.

http://codepad.org/zVFRtoV5

Этот ответ показывает, как стереть элементы при перебореstd::map:

for(map<T, S*>::iterator it = T2pS.begin(); it != T2pS.end(); T2pS.erase(it++)) {
    // wilhelmtell in the comments is right: no need to check for NULL. 
    // delete of a NULL pointer is a no-op.
    if(it->second != NULL) {
        delete it->second;
            it->second = NULL;
    }
}
 Sunny Raj08 янв. 2011 г., 22:37
@toolbox: увеличение его вдвое - проблема, не правда ли? потому что, когда я делаю это, в следующий раз в цикле for он проверяет условие и снова увеличивает итератор. Это означает, что каждый раз, когда я удаляю элемент, я пропускаю элемент рядом с ним.
 marcog08 янв. 2011 г., 22:41
@ Сунил Сиэтот ответ для правильного способа перебратьstd::map и стереть элементы одновременно.
 Dawson08 янв. 2011 г., 22:31
@Sunil Смотрите сообщение о чем-либо; если вы увеличиваете итератор при вызове erase, ваш итератор остается действительным. Просто убедитесь, что в цикле for вы случайно не увеличите итератор дважды. @marcog Это не совсем правильно. Хотя узлы могут быть перебалансированы, их фактические адреса в памяти не меняются; это только их левый / правый / родительский указатели (по крайней мере, для красно-черных деревьев). STH ответ работает.
 Sunny Raj08 янв. 2011 г., 22:26
Поэтому невозможно использовать итератор внутри цикла for и удалять элементы на основе какого-либо условия или, другими словами, если у меня есть 1000 элементов на карте и я хочу удалить элементы, которые удовлетворяют определенному определенному пользователем условию, то каждый раз, когда я удалить элемент я должен разорвать цикл и начать все заново?
 marcog08 янв. 2011 г., 22:30
@ Сунил Сstd::mapответ к сожалению да. Удаление элемента реструктурирует дерево, чтобы сохранить его баланс, поэтому вы даже не можете полагаться на все, что находится справа от удаленного элемента, находящегося в его исходном расположении, как в случае некоторых других контейнеров std, таких какstd::list.

it больше не действует послеmymap.erase(it), Это значит, что он может делать все, что захочет.

«он» по-прежнему указывает на то же место, стирание не обновляет итератор само по себе, вы должны сделать это путем сброса итератора. «это» указывает на старое местоположение, которое было стерто из вектора, но все еще содержит старые данные.

Ваш ответ на вопрос