|
Программирование >> Синтаксис инициирования исключений
Функции 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;
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |