Итераторы — это объекты, позволяющие ссылаться на элементы контейнеров STL подобно указателям. Они предоставляют унифицированный способ доступа к элементам любых контейнеров (vector, list, map и др.). Итераторы бывают нескольких видов:
Особое внимание стоит уделять жизни итераторов:
Пример:
std::vector<int> v = {1,2,3,4,5}; for (auto it = v.begin(); it != v.end(); ++it) { if (*it == 3) { // Удаляем элемент со значением 3 it = v.erase(it); // erase возвращает итератор на следующий элемент --it; // при необходимости скорректировать итератор } }
Вопрос: При вызове std::vector::insert инвалидируются ли итераторы?
Частый ответ: Нет, только при добавлении вне диапазона конца.
Правильный ответ: Все итераторы и ссылки, равные или следующие за позицией вставки, инвалидируются, если емкость контейнера увеличивается. Если же емкости хватает — инвалидируются только итераторы в диапазоне после точки вставки.
Пример:
std::vector<int> v = {1,2,3}; auto it = v.begin() + 1; v.insert(v.begin(), 0); // it здесь может быть инвалидирован!
История: В проекте для итерации по std::vector использовались указатели, и после push_back итерация продолжалась по инвалидированным указателям, что приводило к краху приложения.
История: Разработчик удалял элементы std::list через erase в цикле for(auto it : list), не используя возвращаемый erase итератор, вследствие чего итерация перепрыгивала через элементы и удалялись не все нужные.
История: В коде использовался std::map, и после erase по ключу итератор оставался привязан к удалённому элементу (undefined behavior) — это привело к случайным сбоям при дальнейших обращениях.