На этом шаге мы рассмотрим создание пользовательских итераторов.
Давайте напишем свой итератор. Для этого нужно предоставить варианты трактовки пользовательского итератора, что можно сделать двумя способами.
Для использования первого способа в стандартную библиотеку C++ включен специальный базовый класс iterator<>, содержащий определения типов. Вам остается лишь передать нужные типы:
class MyIterator : public std:iterator <std::bidirectional_iterator_tag, type, std::ptrdiff_t, type*, type&> { . . . . };
Первый параметр шаблона определяет категорию итератора, второй - тип элемента, третий - тип разности, четвертый - тип указателя, а пятый - тип ссылки. Последние три аргумента не обязательны, по умолчанию им присваиваются значения ptrdiff_t, type* и type&. Во многих случаях достаточно следующего определения:
class Mylterator : public std::iterator <std::bidirectional_iterator_tag, type> { . . . . };
Ниже показано, как написать пользовательский итератор на примере итератора вставки для ассоциативных контейнеров. В отличие от итераторов вставки стандартной библиотеки C++ наш итератор не использует позицию вставки.
Реализация класса итератора выглядит так:
#include <iterator> // Шаблон итератора вставки для ассоциативных контейнеров template <class Container> class asso_insert_iterator : public std::iterator <std::output_iterator_tag, void, void, void, void> { protected: Container& container; // Контейнер, в который вставляются элементы public: // Конструктор explicit asso_insert_iterator (Container& c) : container(c) { } // Оператор присваивания // - вставляет значение в контейнер asso_insert_iterator<Container>& operator= (const typename Container::value_type& value) { container.insert(value); return *this; } // Разыменование - пустая операция, которая возвращает сам итератор asso_insert_iterator<Container>& operator* () { return *this; } // Увеличение - пустая операция, которая возвращает сам итератор asso_insert_iterator<Container>& operator++ () { return *this; } asso_insert_iterator<Container>& operator++ (int) { return *this; } }; // Вспомогательная функция для создания итератора вставки template <class Container> inline asso_insert_iterator<Container> asso_inserter (Container& c) { return asso_insert_iterator<Container>(c); }
Класс asso_insert_iterator является производным от класса iterator. Чтобы задать категорию итератора, в iterator передается первый аргумент шаблона output_ iterator_tag. Итераторы вывода используются только для записи, поэтому, как и для всех итераторов вывода, в качестве типов элемента и разности указывается void.
В момент создания итератор сохраняет свой контейнер в переменной container. Все присваиваемые значения вставляются в контейнер функцией insert(). Операторы * и ++ реализуют фиктивные операции, которые просто возвращают сам итератор. При использовании стандартного интерфейса итераторов следующее выражение возвращает *this:
*pos = value
Операция присваивания преобразуется в вызов insert(value) для соответствующего контейнера.
Кроме класса итератора вставки мы также определяем вспомогательную функцию asso_inserter для создания и инициализации итератора. Следующая программа добавляет в множество несколько элементов, используя пользовательский итератор вставки:
//--------------------------------------------------------------------------- #include <vcl.h> #include <iostream> #include <iterator> #include <set> #include <algorithm> #include <conio.h> //необходимо для getch() #include "assoiter.hpp" #pragma hdrstop //--------------------------------------------------------------------------- #pragma argsused using namespace std; std::string ToRus(const std::string &in) { char *buff = new char [in.length()+1]; CharToOem(in.c_str(),buff); std::string out(buff); delete [] buff; return out; } template <class T> inline void PRINT_ELEMENTS (const T& coll, const char* optcstr="") { typename T::const_iterator pos; std::cout << ToRus(optcstr); for (pos=coll.begin(); pos!=coll.end(); ++pos) { std::cout <<*pos <<' '; } std::cout << std::endl; } int main(int argc, char* argv[]) { set<int> coll; // Создание итератора вставки для coll // - неудобный способ asso_insert_iterator<set<int> > iter(coll); // Вставка элементов через обычный интерфейс итераторов *iter = 1; iter++; *iter = 2; iter++; *iter = 3; PRINT_ELEMENTS(coll,"Элементы множества: "); // Создание итератора вставки для coll и вставка элементов // - удобный способ asso_inserter(coll) = 44; asso_inserter(coll) = 55; PRINT_ELEMENTS(coll,"Элементы множества после добавления: "); // Использование итератора вставки с алгоритмом int vals[] = { 33, 67, -4, 13, 5, 2 }; copy (vals, vals+(sizeof(vals)/sizeof(vals[0])), // Источник asso_inserter(coll)); // Приемник PRINT_ELEMENTS(coll,"С использованием итератора вставки: "); getch(); return 0; } //---------------------------------------------------------------------------
Результат выполнения программы выглядит так:
Рис.1. Результат выполнения приложения
Со следующего шага мы начнем рассматривать объекты функций STL.