В этой статье мы подробно рассмотрим итераторы, их типы, преимущества и различные функции, которые они поддерживают.
Что такое итератор? В общем, итератор подобен любому объекту, указывающему на определенный элемент в диапазоне данных, таком как массив или контейнер. Итератор похож на указатель в языке C.
В STL итератор — это объект, который можно использовать для обхода или пошагового перемещения элементов в контейнере с помощью набора операторов, таких как оператор приращения (++) или оператор разыменования (*).
Итераторы имеют решающее значение в программировании STL, поскольку они играют важную роль в подключении алгоритмов к контейнеру в дополнение к доступу и управлению данными, хранящимися внутри контейнеров.
Типы итераторов
В зависимости от функциональности, реализуемой итераторами, они классифицируются на:
- Итераторы ввода и вывода: это наиболее упрощенные типы итераторов. Они наиболее полезны в последовательных операциях ввода-вывода, содержащих однопроходные операции.
- Однонаправленные итераторы: они похожи на итераторы ввода, но имеют направление, то есть прямое направление в том смысле, что их можно использовать для прохода через диапазон в одном направлении. Когда однонаправленные итераторы не являются постоянными, их также можно использовать в качестве выходных итераторов. Большинство стандартных контейнеров STL, по крайней мере, поддерживают однонаправленные итераторы.
- Двунаправленные итераторы: они похожи на однонаправленные итераторы с той лишь разницей, что они двунаправленные. Это означает, что мы можем использовать эти двунаправленные итераторы для обхода диапазона как в прямом, так и в обратном направлении.
- Итераторы произвольного доступа: итераторы произвольного доступа являются самыми мощными среди всех итераторов. Это непоследовательные итераторы. Итераторы произвольного доступа позволяют нам получить доступ к любому случайному значению, применяя смещение к текущему значению без необходимости последовательно проходить через каждый элемент. Они обладают такими же свойствами, как и указатели в C.
Следует отметить, что не все контейнеры STL поддерживают все итераторы. Разные контейнеры поддерживают разные итераторы в зависимости от требований их функциональности.
Ниже приведен список контейнеров, использующих разные итераторы:
Контейнеры | Итераторы |
Stack | Нет итератора |
Queue | Нет итератора |
Priority Queue | Нет итератора |
List | Двунаправленный |
Vector | Произвольный доступ |
Deque | Произвольный доступ |
Map | Двунаправленный |
Multimap | Двунаправленный |
Set | Двунаправленный |
Multiset | Двунаправленный |
Преимущества итераторов
Итераторы чрезвычайно полезны, особенно при программировании с использованием различных диапазонов и контейнеров.
Некоторые из преимуществ использования итераторов в программировании можно резюмировать ниже:
1) Повторное использование кода
Пока мы используем итераторы для доступа к элементам в нашей программе, мы можем просто изменить имя контейнера в нашем определении итератора и использовать остальной код аналогичным образом всякий раз, когда нам нужно изменить контейнер.
Это особенно полезно в сценариях, где мы планируем заменить векторный контейнер контейнером списка. Если бы вместо итераторов мы использовали оператор [], код для доступа к элементам был бы бесполезен при смене контейнеров.
2) Простота и удобство программирования
Итераторы существуют с различными встроенными функциями, которые помогают нам легко и удобно перемещаться по содержимому контейнера и получать к нему доступ.
Например, нам не нужно продолжать проверять конец списка или массива, как мы должны делать при использовании операторов [], и нам нужно изменить программный код, например, когда мы хотим добавить элементы, и нам нужно изменить их для loop (цикл).
При использовании итераторов мы можем напрямую обращаться к функциям begin () и end () итераторов без необходимости следить за тем, когда мы достигаем конца списка, а также нам не нужно менять их для цикла.
3) Динамическое добавление/удаление
При использовании итераторов мы можем легко и динамически добавлять или удалять элементы в контейнере без необходимости сдвигать элементы, как мы это делаем в операторах [].
Давайте рассмотрим это на следующем примере:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#include <iostream> #include <vector> using namespace std; int main() { vector<int> vec1 = { 1, 1, 2 }; // Объявление итератора vector<int>::iterator i; // Вставляемый элемент for (i = vec1.begin(); i != vec1.end(); ++i) { if (i == vec1.begin()) { i = vec1.insert(i, 3); // вставляем 3 в начало vec1 } } // содержание vec1 3 1 1 2 cout<<"Vector contents after addition"; cout<<endl; for (i = vec1.begin(); i != vec1.end(); ++i) { cout << *i << " "; } cout<<endl; for (i = vec1.begin(); i != vec1.end(); ++i) { if (i == vec1.begin() + 1) { i = vec1.erase(i); //при этом удаляется 2-й элемент } } cout<<"Vector contents after deletion"; cout<<endl; for (i = vec1.begin(); i != vec1.end(); ++i) { cout << *i << " "; } cout<<endl; return 0; } |
Вывод данных:
Vector contents after addition 3 1 1 2 Vector contents after deletion 3 1 2 |
Как видно из приведенного выше примера, мы видим, что с помощью итераторов мы можем легко добавлять или удалять элементы из контейнера (в данном случае вектора), не прибегая к сложному программированию смещения элементов и реструктуризации контейнера.
Функции итератора
Поскольку итераторы сами по себе являются встроенными конструкциями, они поддерживают различные операции, которые можно выполнять над объектами итераторов. Эти операции/функции позволяют нам эффективно перемещаться по диапазону, а также манипулировать элементами внутри контейнера.
Теперь мы покажем несколько основных операций, поддерживаемых итераторами:
- begin: возвращает первую или начальную позицию итератора.
- end: возвращает последнюю позицию или позицию «после окончания» итератора.
- prev: возвращает новый итератор после уменьшения количества позиций, указанного в аргументе.
- next: возвращает новый итератор после продвижения или увеличения количества позиций, указанного в аргументе.
- inserter: вставляет элемент в любую заданную позицию в контейнере.
- advance: увеличивает позицию итератора до указанного числа, указанного в аргументе.
Мы покажем использование некоторых из этих функций/операций в программе ниже:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#include<iostream> #include<iterator> #include<vector> using namespace std; int main() { vector<int> v = { 1, 1,2,3,5 }; // объявление итераторов для вектора vector<int>::iterator itr1 = v.begin(); vector<int>::iterator itr2 = v.end(); auto it = next(itr1, 2); // отображение положения итератора cout << "Using next() the new iterator is at: "; cout << *it << " "; cout << endl; auto it1 = prev(itr2, 2); // отображение положения итератора cout << "The position of new iterator using prev() is: "; cout << *it1 << " "; cout << endl; //advance advance(itr1,3); // отображение положения итератора cout << "After advance operation,itr1 is positioned at: "; cout << *itr1 << " "; cout << endl; return 0; } |
Вывод данных:
Using next() the new iterator is at: 2 The position of new iterator using prev() is: 3 After the advance operation,itr1 is positioned at: 3 |
При использовании next() новый итератор находится в: 2, позиция нового итератора при использовании prev(): 3, после операции продвижения itr1 располагается в: 3
Используя приведенную выше программу, вы смогли увидеть использование различных операций итератора.
Итог
Таким образом, мы подошли к концу данной статьи по итераторам.
До сих пор мы обсуждали только основы STL, но начиная со следующей статьи мы начнем с контейнеров STL и их программирования.
С Уважением, МониторБанк