|
Частные копии коллекций Если итератор и его курсор не позволяют вносить изменения в коллекцию, существует простой выход: создать частную копию коллекции в конструкторе итератора. На псевдокоде это выглядит так: class Iterator { private: Collection collection; Cursor location; // Текущая позиция в копии public: Iterator(Collection& c) : collection(c), location(collection.First()) {} bool More(); Foo* Next(); }; Конструктор итератора с помощью конструктора копий класса Collection создает вторую частную копию коллекции. Перед вами - один из редких случаев, когда действительно имеет значение тот факт, что переменные класса конструируются в порядке их перечисления; объект collection должен быть сконструирован раньше объекта location, в противном случае вам предстоят мучения с отладкой функции First(). Коллекции объектов или коллекции указателей? Эта схема обычно используется в ситуациях, когда коллекция состоит из указателей или ссылок на объекты, которые во всем остальном никак не связаны с коллекцией. В других коллекциях вместо указателей или ссылок содержатся собственно объекты. template <class Type, int Size> class Array { private: int Size; // Количество объектов Type Type elements[size]; // Объекты (внутренние) // и т.д. }; Здесь объекты буквально внедряются в коллекцию. Чтобы продублировать коллекцию, вам придется скопировать не только указатели, но и объекты - а это может обойтись слишком дорого. С другой стороны, может возникнуть необходимость в том, чтобы итератор возвращал указатель или ссылку на исходный объект исходной коллекции, а не на копию. В любом случае вариант с частными коллекциями отпадает. Тот же принцип действует каждый раз, когда коллекция представляет собой набор ведущих указателей на ее содержимое. Да, она содержит указатели, а не объекты, однако коллекция имеет право удалять эти объекты, поэтому частная копия будет неустойчивой. Некоторые вопросы управления памятью, связанные с этой проблемой - конкретнее, сборка мусора - рассматриваются в части 4 этой книги. Упрощение частной коллекции Предположим, исходная коллекция представляет собой бинарное дерево или другую сложную структуру данных. Так ли необходимо воспроизводить в копии все дополнительные издержки древовидной структуры, если учесть, что вы не собираетесь пользоваться индексированным доступом? Существует общепринятое решение - создать в качестве частной копии упрощенный вариант коллекции. Это будет проще, если в классе коллекции имеется оператор преобразования, порождающий экземпляр упрощенной коллекции. Вместо конструктора копий коллекции итератор использует ее оператор преобразования: class SimpleCollection; // Упрощенный вариант class ComplexCollection { public: operator SimpleCollection*(); }; Существует и другой, похожий вариант - создать в классе SimpleCollection конструкторы для всех остальных типов коллекций. Однако с точки зрения дизайна такое решение неудачно - каждый раз, когда вы придумаете какую-нибудь новую экзотическую коллекцию, вам придется изменять класс SimpleCollection. Для таких случаев существуют операторы преобразования. Если использовать этот вариант, итератор становится универсальным и подходящим для различных типов коллекций. Итератору не нужно ничего знать об исходной коллекции. Конструктору итератора передается адрес упрощенной коллекции вместо исходной, при этом интерфейс выглядит так: class Iterator { private: SimpleCollection* collection; Cursor location; // Текущая позиция в копии public: Iterator(SimpleCollection* c) : collection(c), location(collection->First()) {} bool More(); bool Next(); }; |
Copyright 2005. Климов Александр. All Right Reserved.