Материалы книги получены с http://www.itlibitum.ru/
Итераторы ведущих указателей
Помните VoidPtrIterator? VoidPtrPool возвращает один итератор для перебора всех указателей с
ненулевыми счетчиками ссылок. Все остается без изменений, однако счетчик ссылок теперь
интерпретируется по-другому. Раньше ненулевой счетчик ссылок означал, что объект не следует уничтожать. Теперь он имеет более узкое значение: объект доступен непосредственно из стека. Все эти объекты сохраняются, поскольку они находятся на периметре, но мы также сохраним объекты с нулевыми счетчиками ссылок, если они доступны косвенно.
Для объектов внутри периметра мы должны перебрать дескрипторы каждого объекта периметра, а затем рекурсивно двигаться внутрь до тех пор, пока не будут перебраны все доступные объекты. Для этого нам придется анализировать объекты одним из описанных выше способов. В данном примере будет использовано сочетание виртуальных функций/итераторов. Для этой цели можно слегка переработать старый интерфейс VoidPtrIterator.
class VoidPtrIterator {
protected:
VoidPtrIterator() {}
public:
virtual bool More() = 0;
virtual VoidPtr* Next() = 0;
};
Теперь пул должен поддерживать два типа итераторов. Один итератор перебирает указатели, находящиеся на периметре (то есть имеющие ненулевые счетчики ссылок). Второй - указатели на объекты, находящиеся в указанной половине. Итератор 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) {}
};
Перебор указателей в объектах
Каждый объект возвращает другой итератор VoidPtrIterator, который перебирает указатели,
доступные непосредственно из объекта. Для каждого класса этот итератор должен быть своим. Далее показан пример.
class MotherOfAllObject { // Базовый класс для всех остальных
public:
virtual VoidPtrIterator* Pointers() = 0;
};
template <class Type>
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> 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 имеет базовые классы, придется организовать вложение итераторов для его собственных указателей и указателей базовых классов.
Назад Содержание Далее
|