итераторы в C++

Итераторы в C++: введение

Всем привет! Изучая контейнеры STL мы использовали новый вид переменных — итераторы. Так давайте узнаем, зачем ими пользуются?

Что такое итератор

Итератор — это такая структура данных, которая используется для обращения к определенному элементу в контейнерах STL. Обычно из используют с контейнерамиset, list , а у вектора для этого применяют индексы.

Кстати по мере того, как мы будем изучать итераторы, вам все больше будет казаться, что итераторы и есть указатели (это мы разберем ниже).

Как создать итератор

Для создания итератора мы должны с самого начала программы подключить библиотеку <iterator>.

Далее для его создании нам потребуется использовать вот эту схему:

  • <контейнер> — указываем требуемый контейнер, на который и будет ссылаться итератор. Например map, vector, list.
  • <его тип> — указываем тип контейнера.

Вам нужно помнить! Если вы создали итератор и случайно ввели не тот тип данных, который указали при создании контейнера, то ваша программа будет работать неправильно и вообще может сломается.

Методы начала и конца контейнеров

У каждого контейнера имеются два метода, которые, как указатели передают итератору начало или конец контейнера — begin() и end().

  • Метод begin() отправит итератор на начала контейнера.
  • А метод end() отправит на конец. А если точнее, то на одну ячейку больше последней. Если мы попытаемся вывести эту ячейку у нас появятся проблемы с компилятором 🙂 .

Их мы можем использовать даже без подключения библиотеки <iterator>, что очень удобно.

Также при инициализации итератора мы можем с самого начала написать, куда он будет указывать:

Итератор на vector

Для итератора на vector вы можете:

  • Выполнять операцию разыменования (обращаться к значению элемента на которое указывает итератор), как мы это делали с указателем.

  • Использовать инкремент (it++, ++it) и декремент (it--, --it).
  • Применять арифметические операции. Так например мы можем сместить итератор на пять ячеек в право, вот так:

  • Сравнивать на равенства.

  • Передать переменной разницу итераторов.

Но о использовании арифметических и сравнительных операциях (>, <, ==) с двумя итераторами, вам нужно кое что знать.

Использовать выше сказанные операции можно только с идентичными итераторами, которые указывают на одинаковый контейнер и тип.

Есть исключение из правил — если вы создадите два одинаковых итератора на map то при сравнивании они не будут одинаковы.

Например, если мы создали два итератора на один и тот же контейнер, но указали для них разный тип данных и решили использовать выше сказанные операции — то компилятор начнет ругаться.

Итератор на list, set, map

Для итераторов на list,set,map немного урезан функционал. Так вы не можете:

  • Использовать арифметические операции.

  • Применять операции сравнения (> и <):

Все остальное можно использовать:

  • Применять инкремент и декремент.

  • Использовать операцию разыменования.

  • Сравнивать два итератора на равенство и неравенства:

Кстати использовать арифметические операции, чтобы увеличить итератор на один, как это делает инкремент — нельзя.

Но вы можете сказать: «Так что мы можем двигать итератор только на один элемент? Это же неудобно!». Да было бы совсем не гибко со стороны C++ делать вот такое, но они позаботились и создали функцию — advanсe(), она заменяет операции увеличения и уменьшения над итераторами.

Вот как она работает:

  • <итератор> — сюда мы должны указать итератор, который и нужно изменить.
  • <значение> — тут мы должны вписать число на которое должны увеличить или уменьшить итератор.

Если мы увеличиваем итератор, то используем оператор + к числу. Но можно и просто записать число без оператора +.

Если же нужно уменьшить итератор, то мы добавляем оператор -.

Как работают итераторы

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

  • В строке 10: создали вектор name_vector.
  • Дальше в последующих трех строках занимаемся его заполнением.
  • В строке 16: создали итератор под именем it.
  • В цикле for мы написали, что итератор указывает на последнюю ячейку вектора. С помощью вот такой не замысловатой конструкции :

Выше мы говорили, что метод end() указывает на одну ячейку больше последней. Поэтому, чтобы обратится к последнему элементу в векторе нам понадобилось отнять 1.

  • Используя операцию разыменования, в теле цикла, мы вывели все элементы.

Вы наверняка заметили, что мы выводим элементы задом наперед:

iterator.cpp
6 4 3
Process returned 0 (0x0) execution time : 0.010 s
Press any key to continue.

Важно знать! Если мы захотели выполнить операцию разыменования для ячейки, у которой индекс больше итератора на 5, то это сделать нужно именно так:

А не вот так;

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

Все потому что, операция разыменования * происходит быстрее, чем операция присваивание.  У этих операций совсем разный приоритет использования. У этой * больше, а у этой + меньше.

Итератор это не указатель

Сейчас после всего узнанного многие могут подумать, что итераторы это и есть указатели. Так как очень много общего между ними.

  • Итератор, как и указатель указывает на какую-то ячейку, а  в указателе может храниться адрес ячейки динамического массива.
  • Чтобы обратится к значению ячейки мы применяем операцию разыменования.

Да итератор это усовершенствованная версия указателя, которая только работает с контейнерами. В некоторых языках указатель называют итератором, но это не значит, что он и есть указатель в C++.

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

Как видим, если итератор был бы указателем он стал бы равен адресу указателя. И смог бы еще вывести этот адрес на экран. А вместо этого мы только словили две ошибки от компилятора.

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




Комментарии к записи “Итераторы в C++: введение”

Добавить комментарий

Ваш адрес email не будет опубликован.

  • Иван:

    «мы выводим элементы задам наперед:». Тут ошибка: задом. Спасибо за статью.

  • Борис:

    Очень много опечаток! Изменение итератора — это на advanse и, тем более, не advense, а advance!

  • VLADOS:

    Посмотрите. Вот этот пример не работает:
    #include
    #include
    #include

    using namespace std;

    int main() {
    setlocale(0, «»);

    vector name_vector;

    name_vector.push_back(3);
    name_vector.push_back(4);
    name_vector.push_back(6);

    vector :: iterator it;

    for (it = name_vector.end() — 1; it >= name_vector.begin(); it—) {
    cout << *it << " ";
    }

    system("pause");
    return 0;
    }
    И проблема не в отсутствии в объявлении вектора.

  • Алексей:

    Также при инициализации итератора мы можем с самого начала написать, куда он будет указывать:

    vector i_am_vector;

    vector :: iterator = i_am_vector.begin();

    А где имя итератора?