С++ - язык, который изучается постепенно.ГЛАВА 15. Ведущие указатели
                   Справочники Всё для создания сайта

Ссылки


Home
Бизнес
Справочники
Советы







Материалы книги получены с http://www.itlibitum.ru/

Ведущие указатели

Как и во многих других стратегиях управления памятью, нам придется хранить множество различных ведущих указателей в одной структуре с возможностью перебора. Напрашивается общий абстрактный базовый класс для всех ведущих указателей. К нашим ведущим указателям предъявляются следующие требования:

1. Ведение счетчика ссылок.

2. Хранение их в специальном пространстве памяти с поддержкой перебора.

3. Вызов деструктора указываемого объекта в деструкторе указателя. В зависимости от

используемого алгоритма сборки мусора мы одновременно пытаемся (или не пытаемся)

вернуть занимаемую объектом память. В нашем примере не стоит беспокоиться о возврате

памяти объекта.

Базовый класс VoidPtr

Ниже показан абстрактный базовый класс, удовлетворяющий этим требованиям.

class VoidPtrPool; // Используется для создания, уничтожения

// и перебора VoidPtr

class VoidPtr {

friend class VoidPtrPool;

private:

unsigned long refcount; // Счетчик ссылок

protected:

void* address; // Адрес указываемого объекта

size_t size; // Размер указываемого объекта в байтах

VoidPtr() : address(NULL), size(0), refcount(0) {}

VoidPtr(void* adr, size_t s) : address(adr), size(s), refcount(0) {}

public:

static VoidPtrPool* pool;

virtual ~VoidPtr() { size = 0; address = NULL; }

void* operator new(size_t)

{

if (pool == NULL)

pool = new VoidPtrPool;

return pool->Allocate();

}

void operator delete(void* space)

{ pool->Deallocate((VoidPtr*)space); }

void Grab() { refcount++; }

void Release()

{

if (refcount > 0) refcount--;

if (refcount <= 0) delete this;

}

};

Шаблон ведущего указателя

Наш ведущий указатель представляет собой шаблон, производный от VoidPtr. Он существует в основном для того, чтобы реализовать оператор -> и виртуальный деструктор, который знает, какой деструктор должен вызываться для указываемого объекта. Я решил запретить копирование и присваивание. При копировании дескриптора должен копироваться адрес ведущего указателя, а не сам ведущий указатель или указываемый объект. Следовательно, нет особой необходимости поддерживать копирование и присваивание для ведущих указателей. Как обычно, существует множество вариаций на тему конструкторов. В данном случае я выбрал ту, в которой конструктор ведущего указателя создает указываемый объект.

template <class Type>

class MP : public VoidPtr {

private: // Чтобы запретить копирование и присваивание

MP(const MP<Type>&) {}

MP<Type>& operator=(const MP<Type>&) { return this; }

public:

MP() : VoidPtr(new Type, sizeof(Type)) {}

virtual ~MP() { ((Type*)address)->Type::~TypeOf(); }

Type* operator->() { return (Type*)address; }

};

Шаблон дескриптора

Это уже знакомый нам шаблон дескриптора с подсчетом ссылок из предыдущей главы.

template <class Type>

class Handle {

private:

MP<Type>* pointer;

public:

Handle() : pointer(new MP<Type>) { pointer->Grab(); }

Handle(const Handle<Type>& h) : pointer(h.pointer)

{ pointer->Grab(); }

Handle<Type>& operator=(const Handle<Type>& h)

{

if (this == &h) return *this;

if (pointer == h.pointer) return *this;

pointer->Release();

h.pointer->Grab();

return *this;

}

MP<Type>& operator->() { return *pointer; }

};

В программе он используется для переменных, ссылающихся на объекты.

class Bar {

private:

H<Foo> foo;

public:

void f();

};

void Bar::f()

{

Handle<Foo> f; // Эквивалентно _____Foo* f = new Foo;

f = foo; // Использует operator=(Handle<Type>(foo));

foo = f; // Использует оператор H<Type>(f)

}

Пул ведущих указателей

Простоты ради мы предположим, что классы, производные от VoidPtr, совпадают по размеру с самим VoidPtr; иначе говоря, в них не добавляются новые переменные. Наша задача упрощается; VoidPtrPool теперь может быть простым связанным списком массивов VoidPtr. Структура массива называется VoidPtrBlock.

struct VoidPtrBlock {

VoidPtrBlock* next; // Следующий блок в списке

VoidPtr slots[BLOCKSIZE]; // Массив позиций

VoidPtrBlock(VoidPtrBlock* next_in_list) : next(next_in_list)

{

// Организовать новые позиции в связанный список

for (int i = 0; i < BLOCKSIZE - 1; i++)

slots[i].address = &slots[i + 1];

slots[BLOCKSIZE - 1].address = NULL;

}

~VoidPtrBlock() { delete next; }

}

class VoidPtrPool {

private:

VoidPtr* free_list; // Список свободных VoidPtr

VoidPtrBlock* block_size; // Начало списка блоков

public:

VoidPtrPool() : block_list(NULL), free_list(NULL) {}

~VoidPtrPool() { delete block_list; }

VoidPtr* Allocate();

void Deallocate(VoidPtr* vp);

};

VoidPtr* VoidPtrPool::Allocate()

{

if (free_list == NULL) // Выделить дополнительный блок

{

block_list = new VoidPtrBlock(block_list);

// Добавить в список

block_list->slots[BLOCKSIZE - 1].address = free_list;

free_list = &block_list->slots[0];

}

VoidPtr* space = (VoidPtr*)free_list;

free_list = (VoidPtr*)space->address;

return space;

}

void VoidPtrPool::Deallocate(VoidPtr* p)

{

vp->address = free_list;

free_list = (VoidPtr*)vp->address;

vp->size = 0;

}

В общем, ничего хитрого. При выделении нового блока мы организуем его позиции связанный список и водружаем поверх списка свободных указателей. Если список свободных указателей пуст, а нам потребовался еще один ведущий указатель, мы выделяем новый блок, а затем берем указатель из списка свободных, в котором к этому времени появились вакансии.

Итератор ведущих указателей

Для перебора всех ведущих указателей мы создадим класс итератора с именем  VoidPtrIterator. VoidPtrPool возвращает итератор, перебирающий все активные указатели (то есть все указатели, не присутствующие в списке свободных). Он будет объявлен как чисто абстрактный базовый класс, поскольку в следующей главе тот же интерфейс будет использован для перебора указателей, внедренных в объекты.

class VoidPtrIterator {

protected:

VoidPtrIterator() {}

public:

virtual bool More() = 0;

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


Назад    Содержание    Далее    

Home  Создание сайтов  Учебник по записи CD  Справочник Web дизайнера Самоучитель IE PHP и MySQL Компьютерные сети С++ E-mail me

Copyright 2007. Климов Александр. All Right Reserved.
Hosted by uCoz