|
Программирование >> Синтаксис инициирования исключений
SH(const WH<Type>& h) : pointer(h.pointer) { pointer->Grab(); } operator WH<Type>() { return WH<Type>(pointer); } SH<Type>& operator=(const SH<Type>& h) if (this == &h) return *this; if (pointer == h.pointer) return *this; pointer->Re1ease(); h.pointer->Grab(); return *this; BMP<Type>& operator->() { return *pointer; } Шаблон используется для обычных переменных (а не для переменных класса), ссылающихся на объекты. Благодаря конструктору, принимающему H<Type>, и операторной функции operator H<Type>() он также может использоваться в операциях присваивания с участием переменных классов, то есть слабых дескрипторов. class Bar { private: WH<Foo> foo; public: void f(); void Bar::f() SH<Foo> f; Эквивалентно Foo* f = new Foo; f = foo; Использует operator=(SH<Type>(foo)); foo = f; Использует operator WH<Type>(f); Итераторы ведущих указателей Помните VoidPtrIterator? VoidPtrPool возвращает один итератор для перебора всех указателей с ненулевыми счетчиками ссылок. Все остается без изменений, однако счетчик ссылок теперь интерпретируется по-другому. Раньше ненулевой счетчик ссылок означал, что объект не следует уничтожать. Теперь он имеет более узкое значение: объект доступен непосредственно из стека. Все эти объекты сохраняются, поскольку они находятся на периметре, но мы также сохраним объекты с нулевыми счетчиками ссылок, если они доступны косвенно. Для объектов внутри периметра мы должны перебрать дескрипторы каждого объекта периметра, а затем рекурсивно двигаться внутрь до тех пор, пока не будут перебраны все доступные объекты. Для этого нам придется анализировать объекты одним из описанных выше способов. В данном примере будет использовано сочетание виртуальных функций/итераторов. Для этой цели можно слегка переработать старый интерфейс VoidPtrIterator . class VoidPtrIterator { protected: VoidPtrIterator() {} public: virtual bool More() = 0; virtual VoidPtr* Next() = 0; Теперь пул должен поддерживать два типа итераторов. Один итератор перебирает указатели, находящиеся на периметре (то есть имеющие ненулевые счетчики ссылок). Второй - указатели на объекты, находящиеся в указанной половине. Итератор VoidPtrPoo1::iterator() из главы 15 заменяется следующим: Включить в класс VoidPtrPool class VoidPtrPool { public: VoidPtrIterator* Reachab1e() { return new ReachableIterator(this); } VoidPtrIterator* InRange(void* low, void* high) { return new RangeIterator(this); } Указатели периметра Один из типов итераторов, возвращаемых пулом, перебирает непосредственно доступные указатели (имеющие ненулевой счетчик ссылок). В сущности, перед нами тот же VoidPtrPoolIterator с одной изменившейся строкой - теперь Advance() пропускает позиции с нулевым счетчиком ссылок. Класс реализован как производный от VoidPtrPoolIterator. class ReachableIterator : public VoidPtrPoolIterator { protected: virtual void Advance() Найти следующую используемую позицию VoidPtrPoolIterator::Advance(); while (block != NULL && b1ock->s1ots[s1ot].refcount == 0); public: Reachab1eIterator(VoidPtrBlock* vpb) : VoidPtrPoolIterator(vpb) {} Недоступные указатели В конце цикла мы должны пройтись по ведущим указателям и найти все те, которые продолжают ссылаться на неактивную половину. Это и будут недоступные объекты. Задачу решает следующий итератор, в котором используется очередное тривиальное переопределение VoidPtrPool Iterator. class InRange : public VoidPtrPoolIterator { private: void* low; Нижний адрес диапазона void* high; Верхний адрес диапазона virtual void Advance() Найти следующую используемую позицию VoidPtrPoolIterator::Advance(); while (block != NULL && (b1ock->s1ots[s1ot].address < low b1ock->s1ots[s1ot].address >= high)); public: InRange(VoidPtrBlock* vpb, void* 1ow addr, void* high addr) : VoidPtrPoolIterator(vpb), 1ow(1ow addr), high(high addr) {} Перебор указателей в объектах Каждый объект возвращает другой итератор VoidPtrIterator , который перебирает указатели, доступные непосредственно из объекта. Для каждого класса этот итератор должен быть своим. Далее показан пример. class MotherOfAllObject { Базовый класс для всех остальных public: virtual VoidPtrIterator* Pointers() = 0; template <c1ass Type> class VoidPtrArrayIterator : public VoidPtrIterator { private: VoidPtr* ptrs[Entries]; int next; Следующая позиция в переборе public: VoidPtrArrayIterator() : next(0) for (int i = 0; i < Entries; ptrs[i] = NULL; VoidPtr*& operator[](uint slot) { return ptrs[s1ot]; } virtual bool More() { return next < Entries; } virtual VoidPtr* Next() { return ptrs[next++]; } Пример класса и итератора class Foo { private: WH<Bar> bar; public: virtual VoidPtrIterator* Pointers() new VoidPtrArrayIterator<1>* iterator = new VoidPtrArrayIterato<1>; iterator[0] = bar.Pointer(); return iterator; VoidPtrArrayIterator сделан на скорую руку и в реальном проекте его использовать не стоит, но по крайней мере он демонстрирует общий принцип. Конечно, его следует дополнить проверками диапазонов и инициированием исключений, если будет затребован VoidPtr* для NULL. Foo::Pointers() показывает общий принцип использования VoidPtrArrayIterator . Для каждого класса мы изменяем размер массива, чтобы он совпадал с количеством WH<Widget> и добавляем для каждого дескриптора по одной строке вида iterator(index++) = widget.Pointer(). Этот шаблон справляется со всеми простыми случаями, в которых нам не приходится беспокоиться о базовых классах. Если Foo имеет базовые классы, придется организовать вложение итераторов для его собственных указателей и указателей базовых классов. Перебор указателей Настал момент собрать все воедино в алгоритме перебора всех доступных объектов. Встречая объект, который в данный момент находится в неактивной половине, мы копируем его в активную половину и изменяем адрес в ведущем указателе на новую копию. Если найденный объект уже находится в активной половине, предполагается, что он уже был скопирован, поэтому мы не тратим время на
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |