- •Итераторы
- •Основные понятия
- •Классификация итераторов
- •Свойства итераторов различных типов
- •Последовательные итераторы
- •Двунаправленные итераторы2
- •Итераторы произвольного доступа3
- •Итераторы ввода
- •Итераторы вывода
- •Использование итераторов
- •Вставка одного или нескольких элементов в позицию, указываемую итератором
- •Адаптеры итераторов и итераторы потоков ввода-вывода
- •Итераторы потоков ввода-вывода
- •Итераторы вставки (insert iterators).
- •Функции advance и distance
- •Теги и свойства итераторов5
- •Как написать свой итератор
Использование итераторов
Каждая коллекция в библиотеке STL(как то:vector, list, queue, deque, map, setи т.д.) предоставляет итераторы для перебора своих элементов. Рассмотрим на конкретном примере:
#include <vector>
#include <iostream>
#include <list>
using namespace std;
template<class iterator>
void print(iterator start, iterator end) // (12)
{
cout<<"-----\n";
for(iterator i=start; i!=end; ++i) // (13)
{
cout<<(*i)<<"\n";
}
cout<<"Done\n";
}
intmain() {
int a[5]={1,2,3,4,5};
vector<int> v(&a[0], &a[5]); // (1)
vector<int>::iterator i; // (2)
list<int> l(v.begin(), v.end()); // (3)
print(v.begin(), v.end()); // (4)
print(l.begin(), l.end()); // (5)
print(v.begin()+1, v.end()); // (6)
print(&a[2], &a[3]); // (7)
print(v.rbegin(), v.rend()); // (8)
vector<int>::const_iterator j=v.begin()+2; // (9)
cout<<"3rd element: "<<(*j)<<"\n"; // (10)
// *j=38; // (11)
return 0;
}
Вывод программы:
-----
1
2
3
4
5
Done
-----
1
2
3
4
5
Done
-----
2
3
4
5
Done
-----
3
Done
-----
5
4
3
2
1
Done
3rd element: 3
Теперь комментарии.
Для начала рассмотрим функцию main.
(1): Создаем vectorиз массива. В классеvectorопределен конструктор:
vector(InputIteratorfirst,InputIteratorlast);
Поскольку &a[0]и&a[5]удовлетворяют всем требованиям итератора ввода, то возможен такой вызов конструктора.
(2): Создаем итератор. В каждом классе коллекций STLопределено несколько типов итераторов, предоставляемых этой коллекцией. В частности, классvectorв этом отношении наиболее универсален и предоставляет следующие типы:const_iterator, const_reverse_iterator, iterator, reverse_iterator.Назначения этих типов итераторов ясны из их названия. Простой классvector<int>::iterator представляет собой изменяемый итератор произвольного доступа.
(3): Создаем список, содержащий те же элементы, что и вектор. v.begin()иv.end()возвращают соответственно итератор, указывающий на первый элемент вектора и на законечный элемент. Классlist также предоставляет возможность конструирования из диапазона итераторов:
list(InputIterator first, InputIterator last);
Уже становится заметна универсальность STLи полезность итераторов: если бы такой возможности не было предоставлено, нам бы пришлось самим писать цикл вставки по одному элементу в список. Гораздо хуже было бы, если бы мы пытались создать, к примеру, список элементов не вектора, а словаря (map) или множества (set) – перебор их элементов крайне нетривиален.
(4): вызываем printдля всего вектора
(5): вызываем printдля всего списка. Как видно, вызовы выглядят абсолютно одинаково.
(6): Печатаем все элементы, кроме первого
(7): Печатаем элементы с 3 по 4 не включая 4. Опять вызов выглядит точно также.
Заметим, что вообще говоря передавая какой-либо функции диапазон итераторов, следует тем или иным образом гарантировать, что диапазон является корректным, т.е. jдостижим изi. В простых программах наподобие данной корректность или некорректность диапазона очевидна, однако в больших программах, где создание и обработка данных отстоят друг от друга в коде на значительное расстояние, следует внимательно относиться к теме корректности диапазонов итераторов.
(8): Используем несколько более интересные итераторы: функции vector::rbegin() иrend() возвращают итераторы, перебирающие элементы в обратном направлении.rbegin() указывает на последний элемент,rend() – на элемент перед первым (законечный в обратном направлении – или предначальный).
Обратите внимание, что v.begin()!=v.rend(), аv.end()!=v.rbegin(), они не только указывают на разные элементы, но и представляют собой объекты разных классов.
Проиллюстрировать это можно так:
begin() end()
rbegin()
(9): Создаем константный итератор, указывающий на 3й элемент вектора.
(10): Доступ на чтение к константному итератору возможен
(11): А на запись – нет, поэтому написанное выражение бы вызвало ошибку компиляции, т.к. operator*() у классаvector<int>::const_iteratorвозвращаетconst int&.
(12): Объявляем функцию, способную работать с любым классом итераторов – печать элементов из некоторого диапазона. В частности, в строке (4) в качестве класса iterator подставляетсяvector<int>::iterator, а в строке (7) –int*.
(13): Видно, что для того, чтобы наша функция работала, необходимо и достаточно, чтобы класс iterator удовлетворял требованиям итератора ввода.