Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Программирование на C / C++ / C++ for real programmers.pdf
Скачиваний:
262
Добавлен:
02.05.2014
Размер:
2.04 Mб
Скачать

121

Возврат в исходную точку

Некоторые классы итераторов содержат функцию, которая возвращает итератор к началу перебора. Я называю ее функцией возврата, Rewind(). Такая возможность поддерживается не всеми коллекциями — например, для потока данных из коммуникационного порта это невозможно.

Ограничение диапазона

Если совокупность объектов, представленных в виде коллекции, упорядочена, итератор должен обладать некоторыми средствами для ограничения перебора конкретным диапазоном объектов.

class Collection { public:

class Iterator { public:

Iterator(Key* low, Key* high); // И т.д.

}; // И т.д.

};

В этом фрагменте low и high определяют минимальное и максимальное значение ключа соответственно. More() и Next() пропускают элементы, не входящие в заданный диапазон со включением границ. Другие вариации на эту тему — «все элементы больше X», «все элементы меньше X» и исключение границ (< вместо <=).

Откат

Итераторы также могут поддерживать функцию Previous() для отката на одну позицию, если такая возможность обеспечивается самой коллекцией. Эта функция часто используется вместе с функцией Current(), которая возвращает то, что Next() возвратила при последнем вызове.

Курсоры

Часто бывает очень полезно объединить концепции, описанные в этой главе, и возвращать из Next() не *-указатель, а курсор, который знает, откуда взялся элемент. Благодаря этому пользователь сможет выполнить удаление, замену или вставку до или после текущего объекта. Возможны два варианта реализации: возвращать курсор из функции Next() или включить «курсороподобные» операции в качестве функций класса самого итератора, работающих с последней полученной позицией. Первый вариант требуется взаимодействия итератора с курсором; во втором они объединяются в один класс.

Итератор абстрактного массива

Перейдем к простому примеру на основе нашего разреженного массива. Классы массива и курсора взяты из предыдущего обсуждения без изменений за исключением того, что класс массива теперь также возвращает итератор лишь для непустых ячеек. Универсальный шаблон итератора не используется, поскольку функция Next() возвращает как индекс, так и объект с этим индексом, а это требует нестандартного интерфейса к Next(). Классы курсора и разреженного массива остались в прежнем виде. Я не утверждаю, что это хороший разреженный массив — однако он обладает достаточно простым дизайном, который не будет нам мешать при обсуждении итераторов.

// SparseArray.h class ArrayCursor; class SparseArray {

friend class ArrayCursor; private:

struct Node { Index index; Foo* content;

122

Node* next;

Node(Index i, Foo* c, Node* n) : index(i), content(c), next(n) {}

};

Node* cells; public:

class Iterator { private:

Node* next; public:

Iterator(Node* first) : next(first) {} bool More() { return next != NULL; ] Foo* Next(Index& index)

{

Foo* object = next->content; index = next->index;

next = next->next; return object;

}

};

Iterator* NonEmpty() { return new SparseArray::Iterator(cells); } SparseArray() : cells(NULL) {}

ArrayCursor operator[](Index i);

};

class ArrayCursor { friend class SparseArray; private:

SparseArray& array; Index index; SparseArray::Node* node;

ArrayCursor(SparseArray& arr, Index i)

:array(arr), index(i), node(NULL) {} ArrayCursor(SparseArray& arr, SparseArray::Node* n)

:array(arr), node(n), index(n->index) {}

public:

ArrayCursor& operator=(Foo* foo);

operator Foo*() { return node != NULL ? node->content : NULL; } Foo* operator->()

{

if (node == NULL)

throw nil_error;

else

return node->current;

}

};

Пожалуй, я бы не рискнул показывать эту программу потенциальному работодателю как доказательство глубоких познаний в С++, но она проста, быстра и справляется со своей задачей. Ниже перечислены некоторые изменения, которые можно было бы внести в коммерческий вариант: