Написание унифицированных функций для итераторов. Пользовательские итераторы

   
На этом шаге мы рассмотрим создание пользовательских итераторов.

   
Давайте напишем свой итератор. Для этого нужно предоставить варианты трактовки пользовательского итератора, что можно сделать двумя
способами.

  • Передача пяти необходимых типов в общей структуре iterator_traits (смотри 233 шаг).
  • Предоставление (частичной) специализации структуры iterator_traits.

   
Для использования первого способа в стандартную библиотеку 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.



Вы можете оставить комментарий, или Трекбэк с вашего сайта.

Оставить комментарий