|
Программирование >> Синтаксис инициирования исключений
PoolMP(Poo1* p) : pointee(new(p) Type) {} ~PoolMP() { pointee->~Type(); } Type* operator->() const { return pointee; } При желании клиент может использовать PoolMP для выделения и освобождения памяти в локальном пуле. Деструктор ведущего указателя вызывает деструктор указываемого объекта, но не освобождает память. Поскольку ведущий указатель не следит за исходным пулом, копирование и присваивание поддерживать не удастся, так как ведущий указатель понятия не имеет, в каком пуле создавать новые копии. Если не считать этих недостатков, перед нами фактически простейший указатель, не отягощенный никакими издержками. На это можно возразить, что копирование и присваивание все же следует поддерживать, но с использование операторов new и delete по умолчанию. В этом случае конструктор копий и оператор = работают так же, как и для обычного ведущего указателя. Обратные указатели на пул Чтобы поддерживать копирование и присваивание в пуле, можно запоминать адрес пула. template <c1ass Type> class PoolMP { private: Type* pointee; Pool* pool; public: PoolMP(Poo1* p) : pointee(new(p) Type), poo1(p) {} ~PoolMP() { pointee->Type::~Type(); } PoolMP(const PoolMP<Type>& pmp) : pointee(new(poo1) Type(*pointee)) {} PoolMP<Type>& operator=(const PoolMP<Type>& pmp) if (this == &pmp) return *this; delete pointee; pointee = new(pool) Type(*pointee); return *this; Type* operator->() const { return pointee; } Это обойдется вам в четыре лишних байта памяти, но не потребует лишних тактов процессора по сравнению с использованием обычных ведущих указателей. Сосуществование с обычными ведущими указателями Предложенное решение отнюдь не идеально. Интерфейс PoolMP открывает многое из того, о чем следовало бы знать только классам. Более того, если вам захочется совместно работать с объектами из пула и объектами, размещенными другим способом (например, с помощью стандартного механизма), начинаются настоящие трудности. Ценой добавления v-таблицы мы сможем значительно лучше инкапсулировать отличия в стратегиях управления памятью. template <c1ass Type> class MP { protected: MP(const MP<Type>&) {} Копирование не разрешено MP<Type>& operator=(const MP<Type>&) { return *this; } Присваивание - тоже MP() {} Используется только производными классами public: virtual ~MP() {} Освобождение выполняется производными классами virtual Type* operator->() const = 0; template <c1ass Type> class DefaultMP : public MP<Type> { private: Type* pointee; public: Defau1tMP() : pointee(new Type) {} Defau1tMP(const Defau1tMP<Type>& dmp) : pointee(new Type(*dmp.pointee)) {} virtual ~Defau1tMP() { delete pointee; } Defau1tMP<Type>& operator=(const Defau1tMP<Type>& dmp) if (this == &dmp) return *this; delete pointee; pointee = new Type(*dmp.pointee); return *this; virtual Type* operator->() const { return pointee; } template <c1ass Type> class LocalPoolMP : public MP<Type> { private: Type* pointee; Pool* pool; public: LocalPoolMP(Poo1* p) : pointee(new(p) Type), poo1(p) [] LocalPoolMP(const LocalPoolMP<Type>& Ipmp) : pointee(new(1pmp.poo1) Type(*1pmp.pointee)), pool(lpmp.pool) {} virtual ~LocalPoolMP() { pointee->Type::~Type(); } LocalPoolMP<Type>& operator=(const LocalPoolMP<Type>& Ipmp) if (this == &1pmp) return *this; pointee->Type::~Type(); pointee = new(pool) Type(*1pmp.pointee); return *this; virtual Type* operator->() const { return pointee; } Теперь DefaultMP и LocalPoolMP можно использовать совместно - достаточно сообщить клиенту, что они принадлежат к типу MP<Type>&. Копирование и присваивание поддерживается для тех классов, которые взаимодействуют с производными классами, но запрещено для тех, которые знают только о базовом классе. В приведенном коде есть одна тонкость: операторная функция LocalPoolMP::operator= всегда использует new(pool) вместо new(lpmp.pool). Это повышает безопасность в тех ситуациях, когда два ведущих указателя поступают из разных областей действия и разных пулов. Невидимые указатели Раз уж мы заплатили вступительный взнос и создали иерархию классов ведущих указателей, почему бы не пойти дальше и не сделать эти указатели невидимыми? Вместо применения шаблона нам придется реализовать отдельный класс указателя для каждого класса указываемого объекта, но это не слишком большая цена за получаемую гибкость. В файле foo.h class Foo { public: static Foo* make(); Использует выделение по умолчанию static Foo* make(Poo1*); Использует пул virtual ~Foo() {} Далее следуют чисто виртуальные функции В файле foo.cpp class PoolFoo : public Foo { private: Foo* foo; Pool* pool; public: PoolFoo(Foo* f, Pool* p) : foo(f), poo1(p) {} virtual ~PoolFoo() { foo->~Foo(); } Переопределения функций класса, делегирующие к foo class PFoo : public Foo { Обычный невидимый указатель class ConcreteFoo : public Foo { ... }; Foo* Foo::make() return new PFoo(new ConcreteFoo); Foo* Foo::make(Poo1* p) return new PoolFoo(new(p) ConcreteFoo, p); Такой вариант намного чище для клиента. Единственное место, в котором клиентский код должен знать что-то о пулах, - создание объекта функцией make(Poo1*). Остальные пользователи полученного невидимого указателя понятия не имеют, находится их рабочий объект в пуле или нет. Стековые оболочки Чтобы добиться максимальной инкапсуляции, следует внести в описанную архитектуру следующие изменения: Сделать Pool чисто абстрактным базовым классом с инкапсулированными производными классами, производящими функциями и т.д. Предоставить функцию static Foo::makePoo1(). Функция make(Poo1*) будет работать и для других разновидностей Pool, но makePoo1() позволяет Foo выбрать производящую функцию Pool, оптимальную для хранения Foo (например, с передачей размера экземпляра).
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |