У меня есть старый проект, который был построен с использованием Visual Studio 2003, и я недавно перекомпилировал его с vs2005. Однако во время выполнения я получаю следующую ошибку:
итератор списка не может быть увеличен
Я проследил программу до этой функции:
void InputQueue::update()
{
list<PCB>::iterator iter;
list<PCB>::iterator iterTemp;
for(iter = begin(); iter != end(); iter++)
{
if (iter->arrivalTime == 0)
{
ReadyQueue::getInstance()->add(*iter);
iterTemp = iter;
iter++;
erase(iterTemp);
}
}
}
Я не специалист по C++, и это все, что мне досталось отладчику VS. Может ли кто-нибудь объяснить мне, в чем проблема?
Спасибо
Обратите внимание, что если iter->arrivalTime == 0
, то итератор списка увеличивается дважды: один раз перед удалением элемента и еще раз в конце цикла.
Если элемент, который нужно удалить, является последним элементом в списке, это, очевидно, не будет работать правильно. Осмелюсь сказать, что он никогда не работал правильно даже в VS2003, но VS2005 предупреждает вас об этом лучше. :-)
Помните, что повторение end()
является неопределенным поведением. Произойти может что угодно, например, сбой программы или (в данном случае) сообщение об ошибке.
Я просто опущу несколько строк вашего кода, чтобы показать, в чем проблема:
for(iter = begin(); iter != end(); iter++) // ***
{
if (iter->arrivalTime == 0)
{
iter++; // ***
}
}
В двух строках с пометкой *** вы увеличиваете итератор. Проблема в том, что во второй из двух строк вы не проверяете, не дошли ли вы до конца контейнера. Фактически, если вы попадаете во внутренний цикл, вы увеличиваете дважды, но проверяете только, можете ли вы увеличивать один раз.
Одно из решений - проверить, находитесь ли вы на end()
, прежде чем выполнять второе приращение, но мне кажется, что вы пытаетесь выполнить ту же операцию, что и я в мой вопрос некоторое время назад, чтобы выполнить фильтрацию элементов из контейнера (карта в этом случае, но то же самое относится к большинству контейнеров STL).
Полагаю, Крис прав. Однако другая проблема может возникнуть из-за того, что вы назначаете итератору. - Гарантированно ли назначаемые итераторы списков? Не глядя на стандарт, я так не думаю, потому что в документации SGI по итераторам нигде не упоминается возможность присваивания.
Хм, это то, что они подразумевают под «многопроходным»? Потому что в противном случае ничего не говорится о присваиваемости итератора (в отличие от его значения!).
Я бы переписал ваш цикл следующим образом:
while (iter != end())
{
if (iter->arrivalTime == 0)
{
ReadyQueue::getInstance()->add(*iter);
iter = erase(iter);
}
else
{
++iter;
}
}
Теперь вы правильно просматриваете список, проверяя каждый индекс.
Вы не увеличиваете итератор в первой части if
Я - iter = стереть (iter). Функция стирания возвращает новый итератор после того, который был только что удален.
Да ладно, не обращай на меня внимания. Имейте в виду, что это не работает с некоторыми типами контейнеров.
Верно, но OP упомянул, что он работал со списками (у которых есть функция стирания).
Это всего лишь заметка на полях, но очень важная.
Я думаю, вы унаследовали от std::ist<PCB>
. Я должен сказать: наследование для повторного использования функций не всегда хорошо для меня. Но поскольку вы также «наследуете» проект, с этим ничего не поделать ...
Наследование реализации, хотя и не идеальное, можно простить, если это только частное наследование. :-)
Если вы получаете «несовместимый итератор списка», это, вероятно, потому, что внутри вашего «ReadyQueue :: getInstance () -> add (* iter);» вы меняете что-то в * iter, что заставляет алгоритм хеширования возвращать для стирания другое значение, чем при вставке.
Могу я предложить более простой алгоритм?
Бесплатная функция std::remove_if
может использоваться для разделения вашего списка на 2 элемента, которые соответствуют или не соответствуют предикату (например, arrivalTime == 0). Он возвращает итератор, разделяющий диапазоны. Затем вы можете вызвать ReadyQueue::getInstance()->add(subrange_begin, subrange_end)
(у вас же такая перегрузка, правда?) и впоследствии стереть поддиапазон.
Как раз тот случай, когда вы можете использовать алгоритмы STL вместо написания собственных циклов.
Основная причина в том, что "list.erase ()" изменит итератор. Правильная запись для цикла "for":
for (list<CMessage*>::iterator it=que.begin(); it!=que.end(); ++it)
{
if (m_type == (*it)->m_type)
{
delete *it;
it=que.erase(it); //"list.erase()" will change the iterator!!!
if (it==que.end()) break; //Check again!!!
//still has side effect here. --it?
}
}
Но у него все еще есть побочный эффект, поэтому решение Марка будет лучшим.
Из sgi.com/tech/stl/Iterators.html кажется, что прямые итераторы назначаются. Итераторы std :: list являются двунаправленными итераторами (sgi.com/tech/stl/List.html, sgi.com/tech/stl/ReversibleContainer.html) и, таким образом, также являются итераторами прямого направления. :-)