1 Итераторы 2 Итераторы Итератор – это объект,
1 Итераторы
2 Итераторы Итератор – это объект, используемый для доступа к последовательности элементов. Например, это могут быть элементы какого-либо контейнера или поток данных от устройства или из файла. Итераторы позволяют достичь большей взаимонезависимости частей библиотеки алгоритм работающий с элементами контейнера через итераторы может быть использован для обработки любого контейнера поддерживающего итераторы для перечисления собственных элементов. это позволяет контейнеру ничего не знать об обрабатывающих его алгоритмах, единственное, что должен обеспечить контейнер – доступ к своим элементам посредством итераторов.
3 Итераторы Концепция итераторов позволяет использовать один и тот же алгоритм сортировки абсолютно без изменений для любого контейнера, допускающего изменение элементов: массива, списка, неупорядоченного бинарного дерева. Также можно заставить итератор работать как фильтр перебирать элементы, удовлетворяющие некоторому условию
4 Итераторы Можно добавить и более сложную функциональность заставить итератор изменять коллекцию. Напомним, что перебираемые элементы вовсе не обязаны храниться в оперативной памяти последовательность данных может быть потоком данных от устройства или последовательностью всех простых чисел. Очевидно реализация таких возможностей без помощи абстрагирования итераторов потребовала бы существенного вмешательства в код всех функций, использующих коллекцию или последовательность.
5 Основные понятия Итераторы STL - это объекты, которые имеют оператор (*), возвращающий текущий элемент последовательности. Этот элемент является объектом некоторого класса или встроенного типа T, называемого типом значения (value type) итератора. Для каждого типа итератора X, для которого определено равенство, имеется соответствующий знаковый целочисленный тип, называемый типом расстояния (distanсe type) итератора.
6 Основные понятия Для перемещения итератора на следующий элемент используется оператор (++). В разных типах итераторов могут быть определены и другие операции: оператор (--) (для двунаправленного итератора), увеличение на произвольное число (итератор произвольного доступа) и т.п. оператор (*) и оператор (++) - это основные операции для всех типов итераторов STL.
7 Основные понятия Значения итератора, для которых оператор (* ) определён, называются разыменовываемыми (dereferenceable). Существуют значения итераторов, указывающие за последний элемент коллекции – так называемые законечные (past-the-end) значения. Законечные значения не являются разыменовываемыми и используются для обозначения конца последовательности. Итератор j называется доступным (reachable) из итератора i, если для итераторов данного типа определена операция равенства и существует конечная (возможно, нулевая) последовательность операций j++; j++; …. , после которой j==i. Если итераторы i и j относятся к разным коллекциям, то ни один из них не доступен из другого. В противном случае если итераторы должны перебирать все элементы коллекции (или одинаковое их подмножество), то очевидно, что либо i доступен из j, либо j доступен из i. Коллекция синоним контейнера - объект, который хранит другие объекты.
8 Основные понятия Последовательность элементов задается парой итераторов [i,j), указывающих на начало и конец. элемент, указываемый i, включается, j – нет, т.е. последним рассматривается элемент, предыдущий j. Большинство алгоритмов библиотеки STL, а также методы классов оперируют с последовательностями такого вида.
9 Основные понятия Пример класс vector содержит метод, который заполняет вектор содержимым, взятым из последовательности элементов заданных парой итераторов [first, last). Элемент, указываемый first включается, а указываемый last нет. template
10 Основные понятия Пример print_sequence не имеет никакой информации о той последовательности, которую обрабатывает, что позволяет обрабатывать с ее помощью любую последовательность представимую парой итераторов typedef int *iterator1; void print_sequence(iterator1 first, iterator1 last) { while(first != last) { cout << *first << endl; first++; } } int array[100] = {/*...*/}; print_sequence(&array[0], &array[100]); // распечатать все print_sequence(&array[10], &array[20]); // распечатать часть } Этот фрагмент иллюстрирует наиболее важные концепции, связанные с итераторами: функция, принимающая в качестве аргумента диапазон итераторов, проверка двух итераторов на равенство, разыменование итератора, продвижение итератора, использование в качестве итератора обычного указателя. Классы и функции, связанные непосредственно с итераторами (без привязки к конкретному контейнеру), находятся в заголовочном файле
11 Классификация итераторов В STL Существует 5 категорий итераторов Итераторы последовательного доступа Двунаправленные итераторы (bidirectional) Итераторы произвольного доступа (random access) Итераторы ввода (input) Итераторы вывода (output) Итераторы также могут быть константными (constant, read-only)- допускает лишь чтение изменяемыми (read/write или write-only) - изменяемый, допускает запись
12 Классификация итераторов Итератор последовательного доступа позволяет осуществлять перебор элементов коллекции лишь последовательно в одном направлении. Например: итератор потока данных от какого-нибудь устройства. Двунаправленные итераторы позволяют просматривать коллекцию в двух направлениях, т.е. для каждого элемента перейти к следующему или предыдущему Например, итератор двухсвязного списка. Итератор произвольного доступа позволяет обратиться к элементу непосредственно по его номеру. Например: обычный массив или vector.
13 Классификация итераторов Итератор ввода представляет собой частный случай последовательного константного итератора. Его важным отличием от вышеупомянутых типов является Если i достижим из j и i!=j, то действительным в общем случае является только i, но не j Два итератора ввода в общем случае равны (и – более того – одновременно действительны) тогда и только тогда, когда это копии одного и того же итератора Алгоритмы, использующие итератор ввода, не должны проходить через одно значение итератора дважды, т.е. должны быть однопроходными. Эти свойства становятся очевидными, если вспомнить, что итератор ввода может быть, к примеру, итератором потока данных: увеличение итератора означает выбор следующего байта из потока и при этом 1) теряется предыдущий байт 2) рассмотрим выражение ++i==++j. ++i и ++j будут в любом случае выполнены последовательно и если первым будет увеличен итератор i, то после этого j будет уже недействительным и для него результат операции инкремента будет неопределен.
14 Классификация итераторов Итератор вывода частный случай последовательного итератора. Помимо свойств, аналогичных вышеописанным свойствам итератора ввода он обладает еще одним: прежде чем увеличивать итератор вывода, необходимо что-нибудь записать в элемент, указываемый им, т.е. последовательность действий j++; j++ недопустима.
15 Классификация итераторов Заметим, что эти итераторы укладываются в следующую иерархию: последовательный итератор - удовлетворяет требованиям итератора ввода и вывода, двунаправленный итератор – удовлетворяет требованиям последовательного, произвольного доступа – удовлетворяет требованиям двунаправленного. Константные итераторы - предоставляют пользователю лишь право чтения указываемых элементов последовательности. Изменяемые итераторы - предоставляют только право записи, предоставляют право и чтения, и записи. Всякий итератор ввода, к примеру, является константным; итератор массива обычно предоставляет право и чтения, и записи; всякий итератор вывода предоставляет только право записи.
16 Свойства итераторов различных типов Поскольку STL – шаблонная библиотека (т.е. инстанцирование шаблонов происходит на этапе компиляции, а не исполнения), свойство итератора, например иметь операцию перехода к следующему элементу (++) означает лишь, что если есть итератор X, то выражение X++ - законно; но не означает, что всегда явно определена такая функция. Выражение X++ законно и в случае, когда X – это int*. Приведем примеры использования различных классов итераторов, а затем формальные спецификации - “скелеты” классов.
17 Последовательные итераторы ForwardIterator it; // Конструктор по умолчанию, без аргументов ForwardIterator it2(it1); //Конструктор копий, it1 и it2 эквивалентны it2=it1; // Присваивание, it1 и it2 эквивалентны if(it1==it2) {} // Проверка на равенство выполняется, если // итераторы указывают на одну и туже // позицию, или оба являются законечными if(it1!=it2) {...} // Проверка на неравенство – просто отрицание // равенства it2=++it1; // Продвинуть it1; it2=новому значению it1 it2=it1++; // Продвинуть it1; it2=старому значению it1 T x=*it1; // Получить значение, указываемого элемента; // возможно только если it1 предоставляет // право чтения *it1=x; // Изменить указываемый элемент; возможно // только если it1 предоставляет право записи
18 Двунаправленные итераторы Итераторы этого типа предоставляются контейнером list Использование - в дополнение к операциям с последовательными итераторами: BidirectionalIterator it1, it2; it2=--it1; // Продвинуть it1 назад; it2=новому значению it2=it1--; // Продвинуть it1 назад; it2=старому значению
19 Итераторы произвольного доступа Итераторы этого типа предоставляются контейнерами vector и deque Использование - в дополнение к операциям с двунаправленным итератором: RandomAccessIterator it1, it2; Distance d; // Тип расстояния между итераторами: обычно // size_t или другой целочисленный тип it1+=d; // Продвинуть итератор вперед на d позиций it1-=d; // Продвинуть итератор назад на d позиций it2=it1+d; // it2=it1, продвинутый вперед на d позиций it2=it1-d; // it2=it1, продвинутый назад на d позиций d=it1-it2; // d=расстояние между it1 и it2 if(it1
20 Итераторы ввода InputIterator it1; - Конструктор без аргументов InputIterator it2(it1); - Конструктор копий it1=it2; - Оператор присваивания if(it1==it2) {...} - Проверка на равенство. Равенство – лишь в том случае, когда it1 – копия it2 (полученная с помощью присваивания или конструктора копий), или наоборот. if(it1!=it2) {...} - Неравенство – отрицание равенства ++it1; - Аналогично таким же операциям для последовательного итератора it1++; x=*it1; - Итератор ввода допускает чтение // *it1=x – некорректно, т.к. итератор ввода не допускает записи
21 Итераторы вывода OutputIterator it1; OutputIterator it2(it1); ++it1; it1++; - После предыдущей строчки эта операция некорректна; отдельно - допустима *it1=x; - Итератор вывода допускает только запись x=*it1; - некорректно, итератор вывода не допускает чтения
22 Использование итераторов #include using namespace std; template
23 Использование итераторов #include using namespace std; template
24 Использование итераторов #include using namespace std; template
25 Использование итераторов #include using namespace std; template
26 Использование итераторов #include using namespace std; template
27 Использование итераторов #include using namespace std; template
28 Использование итераторов #include using namespace std; template
29 Использование итераторов #include using namespace std; template
30 Использование итераторов #include using namespace std; template
31 Использование итераторов #include using namespace std; template
32 Использование итераторов #include using namespace std; template
33 Использование итераторов #include using namespace std; template
34 Использование итераторов #include using namespace std; template
35 Использование итераторов #include using namespace std; template
36 Вставка одного или нескольких элементов в позицию, указываемую итератором Все стандартные последовательные контейнеры содержат следующие методы: iterator insert(iterator where, const value_type &val); Этот метод выполняет вставку элемента в позицию перед позицией, указываемой итератором where. Возвращаемое значение – итератор, указывающий на вставленный элемент. void insert(iterator _Where, size_type _Count, const Type& _Val); Вставка _Count элементов, равных _Val, в позицию перед позицией указываемой итератором _Where.
37 Вставка одного или нескольких элементов в позицию, указываемую итератором template
38 Пример из MSDN: // list_class_insert.cpp // compile with: /EHsc #include #include
39 Все STL-коллекции предоставляют итераторы begin() и end(). Чтобы перебрать элементы любой коллекции, следует написать примерно следующее (псевдокод): collection
40 Адаптеры итераторов Адаптер итератора – это “надстройка” над обычным итератором, обладающая тем же самым интерфейсом, но выполняющая какие-то другие или более сложные действия. Например, reverse_iterator является адаптером для iterator позволяющим просматривать коллекцию в обратном порядке. То есть, обратный итератор, созданный из прямого (обязательно двунаправленного или произвольного доступа), будет при операции ++ вызывать у вложенного объекта – прямого итератора – операцию --, и т.д. Наиболее важные из адаптеров итераторы потоков ввода-вывода итераторы вставки.
41 Итераторы потоков ввода-вывода Библиотека STL позволяет обращаться с потоками файлового и консольного ввода-вывода (а также с любыми другими классами, производными от istream и ostream) как с контейнерами: для них существуют специальные итераторы istream_iterator ostream_iterator. Рассмотрим пример:
42 Итераторы потоков ввода-вывода Нижеприведенная программа вводит с клавиатуры целые числа - коды символов - и печатает символы, соответствующие этим кодам. istream_iterator
43 Итераторы потоков ввода-вывода while(input!=inputEnd) { // Равенство итератора входного потока законечному итератору означает конец данных во входном потоке. Обратим внимание, что итератор inputEnd создавался без аргументов, т.е. он не относится к какому-либо конкретному потоку, тогда как итератор input относится к потоку cin. Тем не менее, библиотека STL устроена так, что когда будет достигнут конец входного потока, итератор input станет равным этому итератору (хоть и не будет его копией): оператор == устроен так, что считает равными все законечные итераторы входных потоков *output=string("Symbol: " + (*input) +"\n"); // *input возвращает очередной элемент из входного потока, присваивание *output записывает элемент в выходной поток ++input; // Передвигаем итераторы на следующий элемент в каждом из потоков ++output; // } На первый взгляд польза таких итераторов кажется небольшой, но она становится очевидной в применении итераторов вместе со стандартными алгоритмами STL, работающим с итераторами.
44 Итераторы потоков ввода-вывода Вывод данных через итератор, приводит к перезаписи элементов контейнера. Если места в контейнере не достаточно, может произойти выход за пределы допустимого диапазона. Пример кода: list
45 Итераторы вставки (insert iterators). STL содержит итераторы вставки, которые позволяют не перезаписывать элементы контейнера, а вставлять их в начало - front_insert_iterator в конец - back_insert_iterator в произвольную позицию - insert_iterator Определения этих итераторов находятся в заголовочном файле
46 Итераторы вставки (insert iterators). В дополнение STL содержит три вспомогательные функции, которые могут быть использованы для создания этих итераторов: back_inserter() для создания back_insert_iterator, front_inserter() для создания front_insert_iterator inserter() для создания insert_iterator.
47 Итераторы вставки (insert iterators). Явно создавать итераторы вставки необходимости нет // Создать итератор вставки в начало контейнера с template
48 Итераторы вставки (insert iterators). Эти функции могут быть использованы следующим образом: list
49 Функции advance и distance STL обеспечивает две шаблонных функции advance и distance: template
50 Функции advance и distance Однако они реализованы по-разному для итераторов произвольного доступа для остальных типов, в результате чего для итераторов произвольного доступа они выполняются за постоянное время (такие итераторы сами предоставляют подобные функции), для других типов – за линейное (например, advance() на n позиций – это n операций ++)
51 Использование функций advance и distance : vector
7708-stl_lekciya2_2013.ppt
- Количество слайдов: 51