Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 [ 63 ] 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82

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



• Переработать старый шаблон MP из предыдущих глав (с операторной функцией operator Type*()), чтобы при выходе из пула и указателей за пределы области действия все необходимое автоматически уничтожалось.

Ниже показан примерный вид полученного интерфейса, с фрагментом клиентского кода и без виртуального оператора =.

В файле foo.h

Подключить объявление чисто абстрактного базового класса #inc1ude "pool.h" class Foo { private:

Foo(const Foo&) {}

Foo& operator=(const Foo&) { return *this; } public:

static Pool* makePoo1(); Создать пул, оптимизированный для Foo static Foo* make(); Не использует пул

static Foo* make(Poo1*); Использует пул И т.д.

Клиентский код void g(Foo*);

void f() {

MP<Poo1> poo1(Foo::makePoo1()); MP<Foo> foo(Foo::make(poo1));

foo->MemberOfFoo(); Использует операторную функцию operator->() g(foo); Использует операторную функцию operator Type*()

Выход из области действия - удаляется сначала foo, затем pool

Перспективы

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

На время забудьте о пулах, временных или иных, и вы увидите разнообразные стратегии применения ведущих указателей для поддержки и инкапсуляции управления памятью в С++.



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 [ 63 ] 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82