C ++ Сегментация при использовании стирания на std :: list - PullRequest
7 голосов
/ 28 февраля 2011

Я пытаюсь удалить элементы из связанного списка C ++, используя erase и итератор списка:

#include <iostream>
#include <string>
#include <list>

class Item
{
  public:
    Item() {}
    ~Item() {}
};

typedef std::list<Item> list_item_t;


int main(int argc, const char *argv[])
{

  // create a list and add items
  list_item_t newlist;
  for ( int i = 0 ; i < 10 ; ++i )
  {
    Item temp;
    newlist.push_back(temp);
    std::cout << "added item #" << i << std::endl;
  }

  // delete some items
  int count = 0;
  list_item_t::iterator it;

  for ( it = newlist.begin(); count < 5 ; ++it )
  {
    std::cout << "round #" << count << std::endl;
    newlist.erase( it );
    ++count;
  }
  return 0;
}

Я получил этот вывод и не могу найти причину:

added item #0
added item #1
added item #2
added item #3
added item #4
added item #5
added item #6
added item #7
added item #8
added item #9
round #0
round #1
Segmentation fault

Возможно, я делаю это неправильно, но все равно буду признателен за помощь. спасибо.

Ответы [ 4 ]

23 голосов
/ 28 февраля 2011

Основная проблема здесь заключается в том, что вы используете значение итератора, it, после того как вы вызвали erase для него.Метод erase делает недействительным итератор и, следовательно, его дальнейшее использование приводит к плохому поведению.Вместо этого вы хотите использовать возврат erase, чтобы получить следующий действительный итератор после стертого значения.

it = newList.begin();
for (int i = 0; i < 5; i++) {
  it = newList.erase(it);
}

Также не помешает включить проверку для newList.end(), чтобы учесть случай, когда в list нет как минимум 5 элементов.

it = newList.begin();
for (int i = 0; i < 5 && it != newList.end(); i++) {
  it = newList.erase(it);
}

Как указал Тим , вот отличная ссылка для erase

3 голосов
/ 28 февраля 2011

Когда вы стираете элемент в позиции it, итератор it становится недействительным - он указывает на фрагмент памяти, который вы только что освободили.

Функция erase(it) возвращает другого итератора, указывающегоследующий элемент в списке.Используйте это!

2 голосов
/ 28 апреля 2012

Я делаю это:

for(list<type>::iterator i = list.begin(); i != list.end(); i++)
{
     if(shouldErase)
     { 
        i = list.erase(i);
        i--;
     }
}

Отредактировано, потому что я тупица, который явно не умеет читать.

2 голосов
/ 28 февраля 2011

Вы лишаете законной силы свой итератор, когда вы erase() в цикле. Было бы проще сделать что-то подобное вместо цикла стирания:

list_item_t::iterator endIter = newlist.begin();
std::advance(endIter, 5);
newList.erase(newlist.begin(), endIter);

Возможно, вас заинтересует идиома erase-remove .

...