247
объекты, находящиеся в указанной половине. Итератор
VoidPtrPool::iterator()
из главы 15
заменяется следующим:
// Включить в класс VoidPtrPool
class VoidPtrPool {
public:
VoidPtrIterator*
Reachable()
{ 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() // Найти следующую используемую позицию
{
do
VoidPtrPoolIterator::Advance();
while (block != NULL && block->slots[slot].refcount == 0);
}
public:
ReachableIterator(VoidPtrBlock* vpb) : VoidPtrPoolIterator(vpb) {}
};
Недоступные указатели
В конце цикла мы должны пройтись по ведущим указателям и найти все те, которые продолжают
ссылаться на неактивную половину. Это и будут недоступные объекты. Задачу решает следующий
итератор, в котором используется очередное тривиальное переопределение
VoidPtrPoolIterator
.
class InRange : public VoidPtrPoolIterator {
private:
void*
low; //
Нижний адрес диапазона
void*
high;
//
Верхний адрес диапазона
virtual void Advance() // Найти следующую используемую позицию
{
do
VoidPtrPoolIterator::Advance();
while (block != NULL &&
(block->slots[slot].address < low ||
block->slots[slot].address
>=
high));
}
public:
InRange(VoidPtrBlock* vpb, void* low_addr, void* high_addr)
: VoidPtrPoolIterator(vpb), low(low_addr), high(high_addr) {}
};
248
Перебор указателей в объектах
Каждый объект возвращает другой итератор
VoidPtrIterator
, который перебирает указатели,
доступные непосредственно из объекта. Для каждого класса этот итератор должен быть своим. Далее
показан пример.
class MotherOfAllObject {
// Базовый класс для всех остальных
public:
virtual VoidPtrIterator* Pointers() = 0;
};
template
class VoidPtrArrayIterator : public VoidPtrIterator {
private:
VoidPtr*
ptrs[Entries];
int
next;
//
Следующая позиция в переборе
public:
VoidPtrArrayIterator() : next(0)
{
for (int i = 0; i < Entries; i++)
ptrs[i]
=
NULL;
}
VoidPtr*& operator[](uint slot) { return ptrs[slot]; }
virtual bool More() { return next < Entries; }
virtual VoidPtr* Next() { return ptrs[next++]; }
};
// Пример класса и итератора
class Foo {
private:
WH
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
и добавляем для
каждого дескриптора по одной строке вида
iterator(index++) = widget.Pointer()
. Этот шаблон
справляется со всеми простыми случаями, в которых нам не приходится беспокоиться о базовых
классах. Если
Foo
имеет базовые классы, придется организовать вложение итераторов для его
собственных указателей и указателей базовых классов.
Перебор указателей
Настал момент собрать все воедино в алгоритме перебора всех доступных объектов. Встречая объект,
который в данный момент находится в неактивной половине, мы копируем его в активную половину и
изменяем адрес в ведущем указателе на новую копию. Если найденный объект уже находится в
активной половине, предполагается, что он уже был скопирован, поэтому мы не тратим время на
1>1> Do'stlaringiz bilan baham: |