227
virtual VoidPtr* Next() = 0;
};
Сам итератор работает весьма прямолинейно. Он просто перебирает блоки в цикле и ищет указатели с
ненулевым значением переменной
size
.
class VoidPtrPoolIterator : public VoidPtrIterator {
private:
VoidPtrBlock*
block;
int
slot;
//
Номер позиции в текущем блоке
virtual void Advance() // Найти следующую используемую позицию
{
while (block != NULL)
{
if (slot >= BLOCKSIZE)
{
block
=
block->next;
slot
=
0;
}
else if (block->slots[slot].size != 0)
break;
slot++;
}
}
public:
VoidPtrPoolIterator(VoidPtrBlock*
vpb)
: block(vpb), slot(0), { Advance(); }
virtual bool More() { return block != NULL; }
virtual VoidPtr* Next()
{
VoidPtr* vp = &block->slots[slot];
Advance();
return
vp;
}
};
Кроме того, мы добавим в
VoidPtrPool
следующую функцию:
VoidPtrIterator* iterator()
{ return new VoidPtrPoolIterator(this); }
Наконец, нам пришлось объявить
VoidPtrPoolIterator
другом
VoidPtr
, чтобы в программе можно
было обратиться к его переменной
size
. Забегая вперед, скажу, что в главе 16 мы воспользуемся этим
итератором для других целей; поэтому функция
Advance()
и объявлена виртуальной, чтобы
производные классы могли добавить свою собственную фильтрацию. Если найденная позиция имеет
нулевое значение
size
, мы пропускаем ее. Во всем остальном работа сводится к простому перебору в
массивах, образующих блоки указателей.
Вариации
Перед тем как описывать сами алгоритмы уплотнения, давайте рассмотрим другие варианты исходной
постановки задачи. Они не оказывают принципиального влияния на архитектуру или алгоритмы, а
только на идиомы С++ в их реализации.
228
Невидимые ведущие указатели
Чтобы не использовать шаблоны дескрипторов и ведущих указателей, можно было организовать
множественное наследование ведущих указателей от
VoidPtr
и гомоморфного базового класса. Иначе
говоря, ведущие указатели становятся невидимыми, как объяснялось в предыдущих главах. В игру
вступают все механизмы, сопутствующие идиоме невидимых указателей (например, производящие
функции).
class Foo {
public:
static Foo* make();
// Возвращает пару указатель-указываемый объект
virtual void Member1() = 0;
//
И т.д. для открытого интерфейса
};
// В файле foo.cpp
class FooPtr : public Foo, public VoidPtr {
public:
FooPtr(Foo* foo, size_t size) : VoidPtr(foo, size) {}
virtual ~FooPtr() { delete (Foo*)address; }
virtual void Member1()
{ ((Foo*)address)->Member1(); }
//
И т.д. для остальных открытых функций
};
class RealFoo : public Foo { ... };
Foo* Foo::make()
{
return new FooPtr(new RealFoo, sizeof(RealFoo));
}
// В клиентском коде
class Bar {
private:
Foo*
foo;
//
На самом деле невидимый указатель
public:
Bar() : foo(Foo::make()) {}
};
Такое решение улучшает инкапсуляцию применения ведущих указателей. Кроме того, оно позволяет
производящим функциям решить, какие экземпляры должны управляться подобным образом, а какие
— с помощью обычных невидимых указателей или даже вообще без указателей. Все прекрасно
работает, пока вы соблюдаете осторожность и выделяете в
VoidPtrPool
достаточно места для
FooPtr
. Помните, что из-за множественного наследования по сравнению с
VoidPtr
размер
увеличивается, по крайней мере, на v-таблицу.
Объекты классов
Возможен и другой вариант — потребовать, чтобы все объекты происходили от общего класса-предка,
способного возвратить объект класса для данного объекта или, по крайней мере, размер объекта. В
этом случае вам уже не придется хранить в указателе объект экземпляра, поскольку его можно будет
получить у объекта класса. Если вы готовы смириться с некоторым насилием в отношении типов в
дескрипторах, это также позволит вам избежать шаблонов второго уровня, используемых для главных
указателей. Вместо
void*
в
VoidPtr
можно будет хранить
CommonBase*
(где
CommonBase
— общий
базовый класс). Мы избавляемся от переменной size, от необходимости иметь шаблон, производный от
VoidPtr
, и от виртуального деструктора в
VoidPtr
, и как следствие — от 4-байтового адреса v-
таблицы. С другой стороны, если управляемые объекты уже содержат v-таблицы и не принуждают вас
к применению множественного наследования, дополнительных затрат не будет.
Do'stlaringiz bilan baham: |