Понимание итераторов в STL - PullRequest
7 голосов
/ 09 апреля 2011

Привет, я немного озадачен итераторами и тем, чем они являются на самом деле .... в C ++ STL

В этом случае я использую список, и я не понимаю, почему вы должны сделатьитератор:
std::list <int>::const_iterator iElementLocator;

для дублирования содержимого списка с помощью оператора разыменования:
cout << *iElementLocator;

после присвоения ему может быть list.begin ();

Пожалуйста, объясните, что такое итератор и почему я должен разыменовать его / использовать его!
Спасибо !!

Ответы [ 6 ]

18 голосов
/ 09 апреля 2011

В STL есть три стандартных блока:

  • Контейнеры
  • Алгоритмы
  • Итераторы

На контейнерах концептуального уровнядержать данныеЭто само по себе не очень полезно, потому что вы хотите сделать что-то с данными;Вы хотите управлять над ним, манипулировать им, запрашивать его, играть с ним.Алгоритмы делают именно это.Но алгоритмы не содержат данных, у них нет данных - им нужен контейнер для этой задачи.Передайте контейнер алгоритму, и у вас будет продолжаться действие.

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

Похоже, что вам нужно было бы написать один и тот же алгоритм для каждого контейнера, чтобы каждая версия алгоритма имела правильный код для обхода контейнера.Но есть лучшее решение: попросить контейнер вернуть объект, который может пройти через контейнер.У объекта были бы интерфейсные алгоритмы, известные.Когда алгоритм просит объект «получить следующий элемент», объект будет соответствовать.Поскольку объект пришел непосредственно из контейнера, он знает, как получить доступ к данным контейнера.И поскольку объект имеет интерфейс, который знает алгоритм, нам не нужно дублировать алгоритм для каждого контейнера.

Это итератор.

Итератор здесь склеивает алгоритмв контейнер, без соединения двух.Итератор связан с контейнером, а алгоритм связан с интерфейсом итератора.Источником магии здесь является действительно шаблонное программирование.Рассмотрим стандартный алгоритм copy():

template<class In, class Out>
Out copy(In first, In last, Out res)
{
    while( first != last ) {
        *res = *first;
        ++first;
        ++res;
    }
    return res;
}

Алгоритм copy() принимает в качестве параметров два итератора, настроенных для типа In, и один итератор типа Out.Копирует элементы, начиная с позиции first и заканчивая непосредственно перед позицией last, в res.Алгоритм знает, что для получения следующего элемента нужно сказать ++first или ++res.Он знает, что для чтения элемента нужно сказать x = *first, а для записи элемента нужно сказать *res = x.Это часть алгоритмов интерфейса, и итераторы принимают их.Если по ошибке итератор не соответствует интерфейсу, то компилятор выдаст ошибку при вызове функции типа In или Out, когда тип не определяет функцию.

6 голосов
/ 09 апреля 2011

Я ленивый. Поэтому я бы не стал описывать, что такое итератор и как он используется, особенно если в Интернете уже есть много статей, которые вы можете прочитать сами.

Вот несколько примеров, которые я могу процитировать для начала, предоставляя ссылки на полные статьи:

MSDN говорит,

Итераторы являются обобщением указатели, абстрагируясь от их требования таким образом, что позволяет C ++ программа для работы с разными структуры данных в едином порядке . Итераторы выступают посредниками между контейнерами и универсальными алгоритмы. Вместо того, чтобы работать на конкретные типы данных, алгоритмы определены для работы на диапазоне определяется типом итератора. любой структура данных, которая удовлетворяет Требования итератора могут затем оперироваться по алгоритму. Там пять типов или категорий итератор [...]

Кстати, похоже, что MSDN взял текст, выделенный жирным шрифтом, из самого стандарта C ++, в частности, из раздела § 24.1 / 1, в котором говорится

Итераторы являются обобщением указатели, которые позволяют программе C ++ работать с разными структурами данных (контейнеры) в едином порядке. К быть в состоянии построить шаблон алгоритмы, которые работают правильно и эффективно на разных типах данных структуры, библиотека не формализуется только интерфейсы, но и семантика и допущения сложности итераторов. Все итераторы, которые я поддерживаю выражение * я, в результате чего значение некоторого класса, перечисления или встроенный тип T, называемый типом значения итератора. Все итераторы я для которым является выражение (* i) .m четко определены, поддерживают выражение я-> м с той же семантикой, что и (*я. Для каждого итератора тип X для какое равенство определено, есть соответствующий целочисленный тип со знаком называется тип разницы итератор.

cplusplus говорит,

В C ++ итератором является любой объект что, указывая на некоторый элемент в диапазон элементов (таких как массив или контейнер), имеет способность перебирать элементы этого диапазон с использованием набора операторов (в минимум, приращение (++) и операторы разыменования (*)).

Наиболее очевидной формой итератора является указатель [...]

И вы также можете прочитать это:

Наберитесь терпения и прочитайте все это. Надеюсь, у вас будет представление о том, что такое итератор в C ++. Изучение C ++ требует терпения и времени.

2 голосов
/ 09 апреля 2011

Итератор не совпадает с самим контейнером.Итератор ссылается на один элемент в контейнере, а также предоставляет способы для доступа к другим элементам.

Рассмотрите возможность создания собственного контейнера без итераторов.Он может иметь функцию size для получения количества элементов, которые он содержит, и может перегрузить оператор [], чтобы позволить вам получить или установить элемент по его позиции.

Но "произвольный доступ" изэтот вид нелегко реализовать на некоторых видах контейнеров.Если вы получите миллионный элемент: c[1000000], и контейнер внутренне использует связанный список, ему придется просканировать миллион элементов, чтобы найти нужный.

Вместо этого вы можете разрешить коллекции собиратьзапомнить «текущий» предмет.Он может иметь такие функции, как start и more и next, чтобы позволить вам перебирать содержимое:

c.start();
while (c.more()) 
{
    item_t item = c.next();

    // use the item somehow
}

Но это помещает «состояние итерации» внутри контейнера.Это серьезное ограничение.Что если вы хотите сравнить каждый элемент в контейнере с любым другим элементом?Это требует двух вложенных циклов, оба перебирают все элементы.Если контейнер сам хранит позицию итерации, у вас нет возможности вложить две такие итерации - внутренний цикл разрушит работу внешнего цикла.

Таким образом, итераторы являются независимой копией состояния итерации.Вы можете начать итерацию:

container_t::iterator i = c.begin();

Этот итератор, i, является отдельным объектом, который представляет позицию в контейнере.Вы можете выбрать все, что хранится в этой позиции:

item_t item = *i;

Вы можете перейти к следующему элементу:

i++;

С помощью некоторых итераторов вы можете пропустить несколько элементов вперед:

i += 1000;

Или получить элемент в некоторой позиции относительно позиции, определенной итератором:

item_t item = i[1000];

А с некоторыми итераторами вы можете двигаться назад.

И вы можете обнаружить, есливы вышли за пределы содержимого контейнера, сравнив итератор с end:

while (i != c.end())

Вы можете думать о end как о возвращении итератора, который представляет позицию, которая находится на одну позицию после последней позициив контейнере.

Важным моментом, который следует учитывать при использовании итераторов (и в целом в C ++), является то, что они могут стать недействительными.Это обычно происходит, например, если вы опорожняете контейнер: любые итераторы, указывающие на позиции в этом контейнере, теперь становятся недействительными.В этом состоянии большинство операций над ними не определены - все может произойти!

2 голосов
/ 09 апреля 2011

Итератор для контейнера STL является указателем на массив. Вы можете думать о них как об объектах указателя на контейнеры STL. В качестве указателей вы сможете использовать их с обозначениями указателей (например, *iElementLocator, iElementLocator++). Как объекты, они будут иметь свои собственные атрибуты и методы (http://www.cplusplus.com/reference/std/iterator).

0 голосов
/ 09 апреля 2011

Я бы посоветовал прочитать о перегрузке операторов в C ++. Это скажет, почему * и -> могут означать практически все. Только тогда вы должны прочитать об итераторах. В противном случае это может показаться очень запутанным.

0 голосов
/ 09 апреля 2011

Уже существует много хороших объяснений итераторов. Просто погуглите.

Один пример .

Если есть что-то конкретное, чего ты не понимаешь, вернись и спроси.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...