Развитие «сознания» класса. Итераторы.

   
На этом шаге мы рассмотрим альтернативный метод доступа к объектам контейнера.

   
Итераторы предоставляют альтернативный метод доступа к объектам контейнера. Итераторами снабжаются
абстрактные контейнеры вроде массивов и очередей, а также такие фундаментальные структуры, как списки и векторы.

   С помощью итератора вы можете "передвигаться" по объектам в структуре
без их удаления. Например, обычно только верхний объект стека доступен для просмотра, для получения же
других объектов необходимо удалять их, вызывая функцию Рор(). С помощью итератора можно
"передвигаться" по стеку для выполнения операций над его содержимым.

   
Простой пример демонстрирует, как использовать итераторы для просмотра объектов в очереди. Как и в программе
DIRECT.CPP, в листинге 1 набор строк запоминается косвенным образом, но на этот раз в
контейнере класса TIQueueAsDoubleList.

    Листинг 1. ITERATE.CPP (демонстрация итераторов)

#include <iostream.h>
#include <cstring.h>
#include <classlib\queues.h>

// Объявление глобального косвенного строкового контейнера очереди
TIQueueAsDoubleList iQueue;

int main()
{
  // Запомнить несколько строк в очереди
  iQueue.Put(new string("Line up"));
  iQueue.Put(new string("for the"));
  iQueue.Put(new string("Magical"));
  iQueue.Put(new string("Tour"));

  // Объявить косвенный строковый итератор для iQueue
  TIQueueAsDoubleListIterator iterator(iQueue);

  // Применить итератор для сканирования по очереди
  cout << endl <<"Обход очереди с помощью итератора:" << endl;
  cout << "------------------------------------" << endl;
  while (iterator!= 0)
    {
	  cout << *iterator.Current() << endl;
	  iterator++;
    }
  // Применить стандартный метод извлечения объектов из очереди
  cout << endl << "Извлекаем объекты очереди:" << endl;
  cout << "------------------------------------" << endl;
  while (!iQueue.IsEmpty())
   {
     string *p = iQueue.Get();
     cout << *p << endl;
     delete p;
   }
  return 0;
}

Текст этого приложения можно взять здесь.

   
Результат работы программы:

Обход очереди с помощью итератора: 
------------------------------------ 
Tour 
Mystery 
Magical 
for the 
Line up 

Извлекаем объекты очереди:
------------------------------------ 
Line up 
for the 
Magical 
Mystery 
Tour 

   
В качестве контейнера в программе объявляется глобальный объект с именем iQueue. Поскольку контейнер -
косвенный (его имя начинается с TI), в нем запоминаются указатели на строковые объекты:

TIQueueAsDoubleList<string> iQueue;

   
Как уже упоминалось, обычно лучше всего пользоваться оператором создания динамических объектов new
для запоминания их в косвенном контейнере. Для добавления нескольких строк в очередь программа передает
указатели, возвращаемые оператором new, функции-члену Put() всех контейнеров очередей.
Функция Put() для очереди имеет такое же значение, как и функция Add() для массивов:

iQueue.Put(new string("Magical")); 
iQueue.Put(new string("Mystery"));  

   
Затем в программе создается итератор класса, разработанный для совместного использования с классом TIQueueAsDoubleList.
Для формирования имени класса добавьте в конце слово Iterator:

TIQueueAsDoubleListIteratоr<string> iterator(iQueue);

   
При использовании таких сложных имен полезно создать более короткий алиас с помощью оператора typedef, например:

typedef TIQueueAsDoubleListIterator<string> TIterator; 

   
Директива typedef означает, что отныне TIterator - синоним предыдущего словосочетания.
Объявление контейнера теперь выглядит намного легче для чтения:

TIterator iterator(iQueue); 

   
В любом случае, объявление создаст итератор (который мы назвали iterator) типа TIQueueAsDoubleListIterator<string>.
Передача объекта iQueue конструктору итератора связывает итератор с этим контейнером. Операции с
использованием итератора будут выполняться над данными контейнера iQueue.

   
Например, выражение iterator.Current() возвращает текущий элемент контейнера Queue - первого
объекта при первом обращении. Если контейнер пуст, итератор вернет нулевой указатель (или нулевую ссылку, если контейнер запоминает сами объекты).

   
При использовании итератора в выражениях имейте в виду, что он возвращает целое значение. Если это значение равно нулю (ложь),
значит итератор завершил сканирование данных в контейнере.

   
С помощью функций и значений итератора можно перемещаться по очереди, отображая объекты контейнера в том
порядке, в котором они запоминались:

  while (iterator != 0)     	       //Использовать перегруженный оператор int()  
 {
  cout << *iterator.Сиrrent() << endl; // Извлечь текущий объект 
  iterator++;	                       // Переместить итератор к следующему объекту 
 }  

   
Во всех итераторах реализован оператор инкремента ++ (как постфиксный, так и префиксный). Как
продемонстрировано в предыдущем фрагменте, вы можете пользоваться выражениями iterator++
(или ++iterator) для перемещения объекта итератора к следующему объекту в контейнере. Оператор
++ возвращает объект того же типа, что и контейнер, - этим фактом вы можете воспользоваться для
эффективной реализации циклов. Установим итератор в начальное положение с помощью обращения к его функции
Restart() и объявим указатель на строку р следующим образом:

  iterator.Restart();	// Установить итератор на первый объект 
string *p;        	// Объявить указатель на строку 

   
Теперь можно написать следующий цикл while (или что-нибудь в этом роде) для перемещения по контейнеру:

  while (р = iterator++)      // Извлечь текущий объект и продвинуть итератор 
  cout << *р << endl;       // Отобразить объект, адресуемый р 

   
В управляющем выражении цикла while указателю р присваивается адрес текущего
строкового объекта, кроме того итератор перемещается к следующему объекту. По достижении последнего объекта
управляющее выражение принимает нулевое значение и цикл завершается. В простом операторе вывода в поток
отображается каждый строковый объект.

   
Если вы скомпилируете предыдущий код, возможно, компилятор выдаст вам предупреждение
"possibly incorrect assignment" ("возможно неверное присваивание"),
поскольку оператор = в данном контексте распознается как возможная ошибка в написании оператора
сравнения ==. Тем не менее, наличие единственного знака = в данном случае
корректно, поскольку мы хотим присвоить значение, возвращаемое итератором, указателю р. Если вас
беспокоит присутствие этого предупреждения, замените предыдущий код следующим фрагментом, он полностью
эквивалентен приведенному выше, хотя и труднее читается:

  while ((р = iterator++)!= 0)  
  cout << *p << endl; 

   
Все итераторы имеют, по крайней мере, один конструктор, функцию Restart() и перегруженные
операторы int() и ++. Некоторые итераторы снабжаются также дополнительными специальными функциями.
Например, в итераторах массивов перегружается функция Restart с двумя параметрами:

void Restart(unsigned start, unsigned stop);

   
Можно передать функции Restart() значения индексов для того, чтобы начать итерационный
процесс в ограниченном диапазоне объектов, хранящихся в контейнере-массиве.

   
На следующем шаге мы рассмотрим принадлежность объектов.



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

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