Программирование >>  Синтаксис инициирования исключений 

1 ... 63 64 65 [ 66 ] 67 68 69 ... 82


Функции Al1ocate() и Dea11ocate() вызываются из перегруженных операторов new и delete соответственно. Такой подход предельно упрощен, но работает он неплохо. Вы можете воспользоваться им для любого сочетания классов, и он будет работать с производными классами, в которых добавились новые переменные. Он также может использоваться в схеме управления памятью на базе ведущих указателей. Существуют многочисленные усовершенствования, которые можно внести в показанную основу:

Ограничить размеры блоков числами, кратными некоторому числу байт, степенями 2 или числами Фибоначчи.

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

Предоставить функцию Flush(), которая при нехватке памяти удаляет все содержимое списков.

В функции Al1ocate() при отсутствии в списке свободного места заданного размера выделить память под массив блоков этого размера вместо одного блока.

Подсчет ссылок

Подсчет ссылок основан на простой идее - мы следим за количеством указателей, ссылающихся на объект. Когда счетчик становится равным 0, объект удаляется. Звучит просто, не правда ли? В определенных условиях все действительно просто, но подсчет ссылок обладает довольно жесткими ограничениями, которые снижают его практическую ценность.

Базовый класс с подсчетом ссылок

Начнем с абстрактного базового класса, от которого можно создать производный класс с подсчетом ссылок. Базовый класс содержит переменную, в которой хранится количество вызовов функции Grab() за вычетом количества вызовов функции Re1ease().

class RefCount { private:

unsigned long count; Счетчик ссылок public:

RefCount() : count(0) {} RefCount(const RefCount&) : count(0) {} RefCount& operator=(const RefCount&)

{ return *this; } Не изменяет счетчик virtual ~RefCount() {} Заготовка void Grab() { count++; } void Re1ease()

if (count > 0) count -- ;

if (count == 0) delete this;

Пока клиентский код правильно вызывает функции Grab() и Re1ease(), все работает абсолютно надежно. Каждый раз, когда клиент получает или копирует адрес объекта, производного от RefCount, он вызывает Grab() . Когда клиент гарантирует, что адрес больше не используется, он вызывает Re1ease(). Если счетчик падает до 0 - бац! Нет больше объекта!

*((void**)space) = f1->top of 1ist; f1->top of 1ist = space;



Недостаток такой методики очевиден - она слишком полагается на соблюдение всех правил программистом. Можно сделать получше.

Укзатели с подсчетом ссылок

Давайте усовершенствуем базовый класс RefCount и создадим модифицированный шаблон умного указателя для любых классов, производных от RefCount.

template <c1ass Type>

class CP { Указатель с подсчетом ссылок private:

Type* pointee; public:

CP(Type* p) : pointee(p) { pointee->Grab(); } CP(const CP<Type>& cp) : pointee(cp.pointee)

{ pointee->Grab(); } ~CP() { ponintee->Re1ease(); } CP<Type>& operator=(const CP<Type>& cp)

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

pointee->Re1ease();

pointee = cp.pointee;

pointee->Grab();

return *this;

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

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

class Foo : public RefCount { private:

Foo(); Вместе с другими конструкторами public:

static CP<Foo> make(); Создаем экземпляр Далее следует интерфейс Foo

Тем самым мы гарантируем, что доступ к Foo будет осуществляться только через указатель с подсчетом ссылок. Обратите внимание: это не ведущий, а самый обычный умный указатель.

Ведущие указатели с подсчетом ссылок

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

template <c1ass Type>

class CMP { Ведущий указатель с подсчетом ссылок private:

Type* pointee;

unsigned long count;



public:

CMP() : pointee(new Type), count(0) {} CMP(const CMP<Type>& cmp)

: pointee(new Type(*(cmp.pointee))), count(0) {} ~CMP() { delete pointee; } Независимо от счетчика

CMP<Type>& operator=(const CMP<Type>& cmp)

if (this == &cmp) return *this; delete pointee;

pointee = new Type(*(cmp.pointee)); return *this;

Type* operator->() const { return pointee; } void Grab() { count++; } void Re1ease()

if (count > 0) count-- ;

if (count <= 0)

delete pointee; delete this;

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

Дескрипторы с подсчетом ссылок

На сцену выходит нечто новое: дескриптор (handle) с подсчетом ссылок. По отношению к шаблону CMP он станет тем же, чем CP был для RefCount, - то есть он автоматически вызывает функции Grab() и Re1ease() в своих конструкторах, деструкторе и операторе =.

template <c1ass Type>

class CH { Дескриптор с подсчетом ссылок private:

CMP<Type>* pointee; public:

CH(CMP<Type>* p) : pointee(p) { pointee->Grab(); }

CH(const CH<Type>& ch) : pointee(ch.pointee) { pointee->Grab(); }

~CH() { pointee->Re1ease(); }

CH<Type>& operator=(const CH<Type>& ch)

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

if (pointee == ch.pointee) return *this;

pointee->Re1ease();

pointee = ch.pointee;

pointee->Grab();

return *this;



1 ... 63 64 65 [ 66 ] 67 68 69 ... 82

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика