|
Убогие, но распространенные варианты Вряд ли вы встретите в коммерческих библиотеках классов итераторы именно в таком виде. У каждого находится свой подход к этой теме. Ниже перечислены некоторые варианты, которые часто встречаются в странствиях по С++, с краткими комментариями по поводу их достоинств и недостатков. Мономорфные активные итераторы вне области действия Даже жалко расходовать замечательный термин на такую простую концепцию. Итераторы называются мономорфными, поскольку в них не используются виртуальные функции, и находятся вне области действия, поскольку они не объявляются вложенными в коллекцию. 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(), обратно к правильному типу - и горе ему, если он что-нибудь напутает. Шаблоны изобретались именно для этой цели, так что теперь подобный бред уже нельзя оправдать. |
Copyright 2005. Климов Александр. All Right Reserved.