120
CollectionIterator iter(collection); //
Создать итератор
while (iter.More())
f(iter.Next());
Просто удивительно, что всего несколько строк программы порождает столько проблем:
•
При использовании класса, производного от
Collection
, каждый клиент должен знать, какие
новые классы итераторов должны использоваться вместо старого
CollectionIterator
.
•
Переменные класса итератора видны всем и каждому. Даже если они не составляют
государственной тайны, весь клиентский код придется перекомпилировать каждый раз,
когда
вам захочется изменить реализацию итератора.
•
Занесение итераторов в стек противоречит некоторым стратегиям многопоточности,
рассматриваемым в следующей главе.
•
Многократное использование такого кода — задача мерзкая.
Учитывая все это, будет намного, намного лучше попросить класс коллекции: «Пожалуйста, сэр,
сделайте мне итератор» вместо того, чтобы самому создавать его в стеке. Невзирая на все проблемы,
этот тип итераторов часто встречается в коммерческих библиотеках классов.
Пассивные итераторы типа void*
Самая распространенная вариация на тему пассивных итераторов — не
возиться с предварительным
объявлением класса итератора, а обмануть клиентов и внушить им, что на самом деле они имеют дело с
типом
void*
. Все это часто маскируется каким-нибудь красивым именем с помощью
typedef
, но
уродливый
void*
так легко не спрячешь.
typedef void* AprilInParis;
class Collection {
public:
AprilInParis
Iterate();
//
Возвращает загримированный void*
bool
More(AprilInParis&);
Foo*
Next(AprilInParis&);
};
Конечно, во внутреннем представлении
хранится что-то более разумное, чем
void*
, поэтому код
реализации
Collection
должен постоянно преобразовывать
void*
к реальности. Не знаю как вас, но
лично меня приводит в ужас одна мысль о том, что клиентский
код будет возиться с
void*
до его
преобразования. К тому же отладка такого кода дьявольски сложна, поскольку отладчик знает о том, с
чем он имеет дело, ничуть не больше клиента. Красивое название итератора не скроет изначального
уродства такого подхода.
Нетипизированные значения функции Next()
Многие классы итераторов пишутся в обобщенной форме для типа
void*
или какого-то абстрактного
базового класса. Клиент должен сам приводить значение, возвращаемое функцией
Next()
, обратно к
правильному типу — и горе ему, если он что-нибудь напутает. Шаблоны изобретались именно для этой
цели, так что теперь подобный бред уже нельзя оправдать.
Лучшие варианты
Начиная с этого места, я буду говорить об активных итераторах, однако все сказанное в равной мере
относится и к пассивным итераторам. Некоторые разновидности итераторов
сильно зависят от
характеристик коллекции, но другие обладают большей универсальностью. Перечисляю их, не
придерживаясь какого-то определенного порядка.