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

1 ... 60 61 62 [ 63 ] 64 65 66 ... 82


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 (например, с передачей размера экземпляра).



1 ... 60 61 62 [ 63 ] 64 65 66 ... 82

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