
- •Итераторы
- •Основные понятия
- •Классификация итераторов
- •Свойства итераторов различных типов
- •Последовательные итераторы
- •Двунаправленные итераторы2
- •Итераторы произвольного доступа3
- •Итераторы ввода
- •Итераторы вывода
- •Использование итераторов
- •Вставка одного или нескольких элементов в позицию, указываемую итератором
- •Адаптеры итераторов и итераторы потоков ввода-вывода
- •Итераторы потоков ввода-вывода
- •Итераторы вставки (insert iterators).
- •Функции advance и distance
- •Теги и свойства итераторов5
- •Как написать свой итератор
Функции advance и distance
STL обеспечивает две шаблонных функции advance и distance:
template <class InputIterator, class Distance>
inline void advance(InputIterator& i, Distance n);
template <class InputIterator, class Distance>
inline void distance(InputIterator first, InputIterator last, Distance& n);
Эти функции позволяют продвинуть итератор любого типа на произвольное количество позиций вперед (advance), а также определить расстояние между двумя итераторами (distance). Однако они реализованы по-разному для итераторов произвольного доступа и для остальных типов, в результате чего для итераторов произвольного доступа они выполняются за постоянное время (такие итераторы сами предоставляют подобные функции), а для других типов – за линейное (например,advance() наnпозиций – этоnопераций ++)
Использование:
vector<int> v;
list<int> l;
// ... Заполнить коллекции
vector<int>::iterator vi=v.begin(), vi2=vi;
list<int>::iterator li=l.begin(), li2=li;
vector<int>::difference_type vdist; // См. Главу 7
list<int>::difference_type ldist;
advance(vi,5);// Выполнится за постоянное время (1 операция +)
vdist=distance(vi,vi2);
advance(li,5);// Выполнится за 5 операций ++
ldist=distance(li,li2);
Теги и свойства итераторов5
Часто бывает нужно для повышения универсальности и независимости алгоритма получить непосредственно из итератора такие его свойства, как: тип, тип итерируемого контейнера, тип разности итераторов и т.д. Для этого предназначены теги (iteratortags) и свойства (traits) итераторов
Рассмотрим определения из файла <xutility>:
// ITERATOR TAGS
structinput_iterator_tag
{ // identifying tag for input iterators
};
structoutput_iterator_tag
{ // identifying tag for output iterators
};
structforward_iterator_tag
: public input_iterator_tag
{ // identifying tag for forward iterators
};
structbidirectional_iterator_tag
: public forward_iterator_tag
{ // identifying tag for bidirectional iterators
};
structrandom_access_iterator_tag
: public bidirectional_iterator_tag
{ // identifying tag for random-access iterators
};
// Часть кода опущена
// TEMPLATE CLASS iterator
template<class _Category, // Сюда подставляется один из ..._iterator_tag
class _Ty,
class _Diff = ptrdiff_t,
class _Pointer = _Ty *,
class _Reference = _Ty&>
structiterator
{ // base type for all iterator classes
typedef _Category iterator_category;
typedef _Ty value_type;
typedef _Diff difference_type;
typedef _Diff distance_type; // retained
typedef _Pointer pointer;
typedef _Reference reference;
};
// Часть кода опущена
// TEMPLATE CLASS iterator_traits
template<class _Iter>
structiterator_traits
{ // get traits from iterator _Iter
typedef typename _Iter::iterator_category iterator_category;
typedef typename _Iter::value_type value_type;
typedef typename _Iter::difference_type difference_type;
typedef difference_type distance_type; // retained
typedef typename _Iter::pointer pointer;
typedef typename _Iter::reference reference;
};
// iterator_category– категория (тип, тег) итератора
// value_type– класс указываемого объекта
// difference_type– класс для представления разности итераторов
// distance_type– то же самое (для совместимости со старыми версиямиSTL)
// pointer– указатель на указываемый объект
// reference- ссылка
// Далее:
template<class _Ty>
structiterator_traits<_Ty *>
{ // Класс характеристик итераторов для встроенных указателей
// вспомним, что встроенные указатели имеют все свойства
// итераторов произвольного доступа
typedef random_access_iterator_tag iterator_category;
typedef _Ty value_type;
typedef ptrdiff_t difference_type;
typedef ptrdiff_t distance_type; // retained
typedef _Ty *pointer;
typedef _Ty& reference;
};
template<class _Ty>
struct iterator_traits<const _Ty *>
{ // Класс характеристик итераторов для встроенных константный указателей
// вспомним, что встроенные указатели имеют все свойства
// итераторов произвольного доступа
typedef random_access_iterator_tag iterator_category;
typedef _Ty value_type;
typedef ptrdiff_t difference_type;
typedef ptrdiff_t distance_type; // retained
typedef const _Ty *pointer;
typedef const _Ty& reference;
};
О том, зачем нужны такие сложные с виду конструкции – см. чуть далее.
Все STL-итераторы наследуются от классаiterator, поэтому по имеющемуся итератору классаXможно получить его категорию:X::iterator_categoryи т.п.
Кстати, можно заметить в определениях xxx_iterator_tagв явном виде описанную в части «Классификация итераторов» иерархию.
Пример кода: Попробуем создать максимально универсальную функцию, которая суммирует каждый n-й элемент контейнера. Сначала код, потом комментарии.
#include <vector>
#include <list>
#include <iostream>
#include <iterator>
using namespace std;
template<class iter>
voidsumEachNth(iter start, iter end,
typename iterator_traits<iter>::difference_type n,
typename iterator_traits<iter>::value_type &startValue) {
return _sumEachNth(start,end,n,startValue,
iterator_traits<iter>::iterator_category());
}
template<class iter>
void_sumEachNth(iter start, iter end,
typename iterator_traits<iter>::difference_type n,
typename iterator_traits<iter>::value_type &startValue,
random_access_iterator_tag)
{
iter i;
for(i=start; i<end; i+=n) startValue+=*i;
}
template<class iter>
void_sumEachNth(iter start, iter end,
typename iterator_traits<iter>::difference_type n,
typename iterator_traits<iter>::value_type &startValue,
input_iterator_tag)
{
iterator_traits<iter>::difference_type zero(0);
iterator_traits<iter>::difference_type i=zero;
iter it;
for(it=start; it!=end; ++it, ++i) {
if(i==n) {
i=zero;
}
if(i==zero) startValue+=*it;
}
}
intmain() {
int a[7]={1,2,3,4,5,6,7};
list<int> l(a,a+7);
int s=0;
sumEachNth(a,a+7,3,s); // (1)
cout<<"Sum="<<s<<"\n";
s=0;
sumEachNth(l.begin(),l.end(),3,s); // (2)
cout<<"Sum="<<s<<"\n";
return 0;
}
Вывод программы:
Sum=12
Sum=12
Тут используется сразу 2 очень интересных и распространенных трюка. Присутствует 2 реализации функции _sumEachNth: одна – менее эффективная – для произвольного типа итераторов ввода, вторая – более эффективная – для итераторов произвольного доступа. У этих функций разный тип последнего параметра, поэтому это – разные функции для компилятора.
Посмотрим, что делает компилятор в строках (1) и (2).
В строке (1) вызывается sumEachNth для типа итератораint*. Согласно определения из<xutility>
template<class_Ty>
structiterator_traits<_Ty *>
{ // get traits from pointer
typedef random_access_iterator_tag iterator_category;
typedef _Ty value_type;
typedef ptrdiff_t difference_type;
// ...
};
Следовательно iterator_traits<int*>::iterator_category == random_access_iterator_tag, т.е. int* работает как итератор произвольного доступа. Следовательно, в этом случае будет выбрана первая реализация_sumEachNth, принимающая в качестве последнего параметраrandom_access_iterator_tag.
template<class _Iter>
structiterator_traits
{ // get traits from iterator _Iter
typedef typename _Iter::iterator_category iterator_category;
typedef typename _Iter::value_type value_type;
typedef typename _Iter::difference_type difference_type;
typedef difference_type distance_type; // retained
typedef typename _Iter::pointer pointer;
typedef typename _Iter::reference reference;
};
Для списка же категория итератора – list<int>::iterator::iterator_category==(из файла <list>, не будем приводить код)bidirectional_iterator_tag, который может быть приведен кinput_iterator_tag(так как он унаследован от него) и будет выбрана вторая реализация.
Мощь шаблонов С++ в основном и состоит в частичной специализации и мощной системе поиска оптимального соответствия типов параметров функций. Именно благодаря этим двум вещам удалось создать различные характеристики (traits) для итераторов, являющихся указателями и для обычных итераторов, а также создать разные реализации функции для разных типов итераторов без лишних проблем. Не будь их, мы бы не смогли использовать в функцииsumEachNth простые указатели типаint*, поскольку это – не объекты и для них, в отличие отSTL-итераторов, не определена явноiterator_category.