ПрограммированиеC++ разработчик

Что такое итераторы в C++ STL, какие их виды существуют и какие нюансы их использования нужно знать при программировании?

Проходите собеседования с ИИ помощником Hintsage

Ответ

Итераторы — это объекты, позволяющие ссылаться на элементы контейнеров STL подобно указателям. Они предоставляют унифицированный способ доступа к элементам любых контейнеров (vector, list, map и др.). Итераторы бывают нескольких видов:

  • InputIterator — для чтения последовательности вперед.
  • OutputIterator — для записи значений последовательности.
  • ForwardIterator — для чтения и записи вперед (живет дольше, чем OutputIterator).
  • BidirectionalIterator — позволяет передвигаться как вперед, так и назад.
  • RandomAccessIterator — поддерживает произвольный доступ (например, std::vector::iterator).

Особое внимание стоит уделять жизни итераторов:

  • Итераторы контейнеров могут инвалидироваться (становиться недействительными) при изменении контейнера. Например, вставка элементов в вектор может сделать старые итераторы недействительными.
  • Разные контейнеры по-разному управляют жизненным циклом итераторов. У std::list вставка или удаление не инвалидируют другие итераторы, у std::vector — почти всегда инвалидируют.

Пример:

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) — это привело к случайным сбоям при дальнейших обращениях.