Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Программирование на C / C++ / C++ for real programmers.pdf
Скачиваний:
262
Добавлен:
02.05.2014
Размер:
2.04 Mб
Скачать

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-таблицы и не принуждают вас к применению множественного наследования, дополнительных затрат не будет.