Правила инвалидации итератора

Каковы обычные правила аннулирования Iterator при работе с классами контейнеров STL (Vector, Dequeue, list, map, multimap, set, multiset). Можно ли классифицировать и обобщить некоторые общие правила/рекомендации, о которых должен знать программист C++ STL при работе с контейнерами и их итераторами?


person Alok Save    schedule 06.11.2010    source источник
comment
Цитата: В общем, простые мутации, которые не изменяют форму контейнера (например, замена третьего элемента массива новым значением), не вызывают проблем. c2.com/cgi/wiki?IteratorInvalidationProblem   -  person rwong    schedule 06.11.2010
comment
Правила аннулирования итератора   -  person Lightness Races in Orbit    schedule 22.06.2011
comment
@Tomalak Geret'kal: Это хорошо! Могу ли я предложить добавить его как запись c++ faq.   -  person Alok Save    schedule 22.06.2011
comment
@Als: ОК!   -  person Lightness Races in Orbit    schedule 22.06.2011
comment
@Tomalak Geret'kal: Ах, простите, сегодня я немного занят работой, за которую немного платят ;) не заметил, что вы уже это сделали! Замечательно.   -  person Alok Save    schedule 22.06.2011
comment
@Als: Спасибо за первоначальную идею, кстати :)   -  person Lightness Races in Orbit    schedule 22.06.2011
comment
Гораздо более запутанный ответ на SO: stackoverflow.com/questions/6438086/iterator-invalidation -правила   -  person Steed    schedule 31.03.2014


Ответы (2)


Эти правила зависят от контейнера. На самом деле, это важные критерии для принятия решения о том, какой контейнер вы используете.

Например, итераторы для std::vector могут стать недействительными при вставке объекта (зависит от того, куда вставляется объект и происходит ли перераспределение), и они становятся недействительными, когда объект удаляется до итератора. У std::list такой проблемы нет. Вставка и удаление объектов (кроме объекта, на который указывает итератор) не делает итератор недействительным.

SGI предоставляет хорошую документацию по этому вопросу.

person Björn Pollex    schedule 06.11.2010
comment
Хорошо, можешь добавить немного. - person Alok Save; 06.11.2010
comment
Предпочитайте стандарт документам SGI (которые предназначены для более старого оригинального STL, а не для стандартных библиотек). Я использую документы SGI для быстрой справки, но если вы хотите быть уверенным, то всегда есть шанс, что стандарт что-то изменил. - person Steve Jessop; 06.11.2010
comment
@Steve: Вы правы, только у некоторых из нас нет стандарта. - person Björn Pollex; 06.11.2010
comment
Найдите ISO/IEC 14882:2003(E). Или, если программирование на C++ стоит вам 26 евро + почтовые расходы, amazon.co.uk/Standard-Incorporating-Technical-Corrigendum-No/dp/ ;-) - person Steve Jessop; 06.11.2010

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

Стандарт C++ определяет поведение iterator таким образом, что позволяет реализовать его с помощью простых указателей C. Это не требует, чтобы библиотека фактически использовала указатели; это просто позволяет сделать это.

По сути, итератор становится недействительным, если операция приводит к освобождению или смещению базового элемента хранилища (массива кучи, используемого в vector, узла связанного списка, используемого в list, или узла дерева, используемого в map или set). в другую ячейку памяти.

vector обычно реализуется путем выделения массива из динамической памяти (кучи). Чтобы уменьшить количество перераспределений, массив всегда выделяется с некоторым резервом, т.е. изначально неиспользуемым пространством. По мере добавления элементов в массив незанятое пространство используется. Когда все свободное пространство занято и необходимо вставить новый элемент, будет выделен новый массив большего размера. Это приведет к аннулированию всех итераторов, указывающих на старый массив.

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

Для list, map и set узел дерева или узел списка остается действительным до тех пор, пока содержащийся в нем элемент не будет стерт. Обратите внимание, что итератор, указывающий на недействительный узел, нельзя использовать ни для чего; даже не для увеличения/уменьшения итератора. Это связано с тем, что в реализации связанного списка или дерева итератор зависит от дочерних указателей, которые хранятся в самом узле.

Чтобы всегда гарантировать правильную работу, стандарт сформулирован более строго, чем если бы использовались простые структуры данных (что, как это ни парадоксально, дает больше свободы разработчикам библиотек для использования более сложных структур данных). Например, см. http://c2.com/cgi/wiki?IteratorInvalidationProblem и http://www.threadingbuildingblocks.org/codesamples.php .

person rwong    schedule 06.11.2010
comment
Примечание: несмотря на то, что элементы в двухсторонней очереди не перемещаются из-за push_back(), push_front(), emplace_back() или emplace_front(), эти операции могут сделать итераторы недействительными. - person Nevin; 12.04.2013