Читайте также:
|
|
исключения память, занимаемая объектом-итератором, вообще никогда не будет освобождена.
Эту ситуацию помогает исправить паттерн заместитель. Вместо настоящего итератора мы используем его заместителя, память для которого выделена в стеке. Заместитель уничтожает итератор в своем деструкторе. Поэтому, как только заместитель выходит из области действия, вместе с ним уничтожается и настоящий итератор. Заместитель гарантирует выполнение надлежащей очистки даже при возникновении исключений. Это пример применения хорошо известной в C++ техники, которая называется «выделение ресурса - это инициализация» [ES90]. В разделе «Пример кода» она проиллюстрирована подробнее;
а итераторы могут иметь привилегированный доступ. Итератор можно рассматривать как расширение создавший его агрегат. Итератор и агрегат тесно связаны. В C++ такое отношение можно выразить, сделав итератор другом своего агрегата. Тогда не нужно определять в агрегате операции, единственная цель которых - позволить итераторам эффективно выполнить обход. Однако наличие такого привилегированного доступа может затруднить определение новых способов обхода, так как потребуется изменить интерфейс агрегата, добавив в него нового друга. Для того чтобы решить эту проблему, класс Iterator может включать защищенные операции для доступа к важным, но не являющимся открытыми членам агрегата. Подклассы класса Iterator (и только его подклассы) могут воспользоваться этими защищенными операциями для получения привилегированного доступа к агрегату;
а итераторы для составных объектов. Реализовать внешние агрегаты для рекурсивно агрегированных структур (таких, например, которые возникают в результате применения паттерна компоновщик) может оказаться затруднительно, поскольку описание положения в структуре иногда охватывает несколько уровней вложенности. Поэтому, чтобы отследить позицию текущего объекта, внешний итератор должен хранить путь через составной объект Composite. Иногда проще воспользоваться внутренним итератором. Он может запомнить текущую позицию, рекурсивно вызывая себя самого, так что путь будет неявно храниться в стеке вызовов.
Если узлы составного объекта Composite имеют интерфейс для перемещения от узла к его братьям, родителям и потомкам, то лучшее решение дает итератор курсорного типа. Курсору нужно следить только за текущим узлом, а для обхода составного объекта он может положиться на интерфейс этого узла.
Составные объекты часто нужно обходить несколькими способами. Самые распространенные - это обход в прямом, обратном и внутреннем порядке, а также обход в ширину. Каждый вид обхода можно поддержать отдельным итератором;
а пустые итераторы. Пустой итератор Nulllterator - это вырожденный итератор, полезный при обработке граничных условий. По определению, Nulllterator всегда считает, что обход завершен, то есть его операция IsDone неизменно возвращает истину.
Паттерн Iterator
Применение пустого итератора может упростить обход древовидных структур (например, объектов Composite). В каждой точке обхода мы запрашиваем у текущего элемента итератор для его потомков. Элементы-агрегаты, как обычно, возвращают конкретный итератор. Но листовые элементы возвращают экземпляр Nulllterator. Это позволяет реализовать обход всей структуры единообразно.
Пример кода
Рассмотрим простой класс списка List, входящего в нашу базовую библиотеку (см. приложение С) и две реализации класса Iterator: одну для обхода списка от начала к концу, а другую - от конца к началу (в базовой библиотеке поддержан только первый способ). Затем мы покажем, как пользоваться этими итераторами и как избежать зависимости от конкретной реализации. После этого изменим дизайн, дабы гарантировать корректное удаление итераторов. А в последнем примере мы проиллюстрируем внутренний итератор и сравним его с внешним.
а интерфейсы классов List и Iterator. Сначала обсудим ту часть интерфейса класса List, которая имеет отношение к реализации итераторов. Полный интерфейс см. в приложении С:
template <class Item> class List { public:
List (long size = DEFAULT_LIST_CAPACITY);
long Count() const;
Item& Get(long index) const;
//...
};
В открытом интерфейсе класса List предусмотрен эффективный способ поддержки итераций. Его достаточно для реализации обоих видов обхода. Поэтому нет необходимости предоставлять итераторам привилегированный доступ к внутренней структуре данных. Иными словами, классы итераторов не являются друзьями класса List. Определим абстрактный класс Iterator, в котором будет объявлен интерфейс итератора:
template <class Item> class Iterator { public:
virtual void First() = 0;
virtual void Next() = 0;
virtual bool IsDoneO const = 0;
virtual Item Currentltemf) const = 0; protected:
Iterator));
};
а реализации подклассов класса Iterator. Класс List Iterator является подклассом Iterator:
Дата добавления: 2015-09-11; просмотров: 81 | Поможем написать вашу работу | Нарушение авторских прав |