Материалы книги получены с http://www.itlibitum.ru/
Убогие, но распространенные варианты
Вряд ли вы встретите в коммерческих библиотеках классов итераторы именно в таком виде. У каждого находится свой подход к этой теме. Ниже перечислены некоторые варианты, которые часто встречаются в странствиях по С++, с краткими комментариями по поводу их достоинств и недостатков.
Мономорфные активные итераторы вне области действия
Даже жалко расходовать замечательный термин на такую простую концепцию. Итераторы называются мономорфными, поскольку в них не используются виртуальные функции, и находятся вне области действия, поскольку они не объявляются вложенными в коллекцию.
class Collection { ... };
class CollectionIterator {
private:
Collection* coll;
public:
CollectionIterator(Collection* coll);
bool More();
Foo* Next();
};
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(), обратно к правильному типу - и горе ему, если он что-нибудь напутает. Шаблоны изобретались именно для этой цели, так что теперь подобный бред уже нельзя оправдать.
Назад Содержание Далее
|