201
PoolMP(Pool* p) : pointee(new(p) Type) {}
~PoolMP() { pointee->~Type(); }
Type* operator->() const { return pointee; }
};
При желании клиент может использовать
PoolMP
для выделения и освобождения памяти в локальном
пуле. Деструктор ведущего указателя вызывает деструктор указываемого объекта, но не освобождает
память. Поскольку ведущий указатель не следит за исходным пулом, копирование и присваивание
поддерживать не удастся, так как ведущий указатель понятия не имеет, в каком пуле создавать новые
копии. Если не считать этих недостатков, перед нами фактически простейший указатель, не
отягощенный никакими издержками.
На это можно возразить, что копирование и присваивание все же следует поддерживать, но с
использование операторов
new
и
delete
по умолчанию. В этом случае конструктор копий и оператор
=
работают так же, как и для обычного ведущего указателя.
Обратные указатели на пул
Чтобы поддерживать копирование и присваивание в пуле, можно запоминать адрес пула.
template
class PoolMP {
private:
Type*
pointee;
Pool*
pool;
public:
PoolMP(Pool* p) : pointee(new(p) Type), pool(p) {}
~PoolMP() { pointee->Type::~Type(); }
PoolMP(const PoolMP& pmp) : pointee(new(pool) Type(*pointee)) {}
PoolMP& operator=(const PoolMP& pmp)
{
if (this == &pmp) return *this;
delete
pointee;
pointee = new(pool) Type(*pointee);
return
*this;
}
Type* operator->() const { return pointee; }
};
Это обойдется вам в четыре лишних байта памяти, но не потребует лишних тактов процессора по
сравнению с использованием обычных ведущих указателей.
Сосуществование с обычными ведущими указателями
Предложенное решение отнюдь не идеально. Интерфейс
PoolMP
открывает многое из того, о чем
следовало бы знать только классам. Более того, если вам захочется совместно работать с объектами из
пула и объектами, размещенными другим способом (например, с помощью стандартного механизма),
начинаются настоящие трудности. Ценой добавления v-таблицы мы сможем значительно лучше
инкапсулировать отличия в стратегиях управления памятью.
template
class MP {
protected:
MP(const
MP&)
{}
//
Копирование не разрешено
MP&
operator=(const
MP&)
{ return *this; } // Присваивание – тоже
MP()
{}
//
Используется только производными классами
202
public:
virtual ~MP() {}
// Освобождение выполняется производными классами
virtual Type* operator->() const = 0;
};
template
class DefaultMP : public MP {
private:
Type*
pointee;
public:
DefaultMP() : pointee(new Type) {}
DefaultMP(const
DefaultMP&
dmp)
: pointee(new Type(*dmp.pointee)) {}
virtual ~DefaultMP() { delete pointee; }
DefaultMP& operator=(const DefaultMP& dmp)
{
if (this == &dmp) return *this;
delete
pointee;
pointee = new Type(*dmp.pointee);
return
*this;
}
virtual Type* operator->() const { return pointee; }
};
template
class LocalPoolMP : public MP {
private:
Type*
pointee;
Pool*
pool;
public:
LocalPoolMP(Pool*
p)
: pointee(new(p) Type), pool(p) []
LocalPoolMP(const LocalPoolMP& lpmp)
: pointee(new(lpmp.pool) Type(*lpmp.pointee)), pool(lpmp.pool) {}
virtual ~LocalPoolMP() { pointee->Type::~Type(); }
LocalPoolMP& operator=(const LocalPoolMP& lpmp)
{
if (this == &lpmp) return *this;
pointee->Type::~Type();
pointee = new(pool) Type(*lpmp.pointee);
return
*this;
}
virtual Type* operator->() const { return pointee; }
};
Теперь
DefaultMP
и
LocalPoolMP
можно использовать совместно — достаточно сообщить клиенту,
что они принадлежат к типу
MP&
. Копирование и присваивание поддерживается для тех
классов, которые взаимодействуют с производными классами, но запрещено для тех, которые знают
только о базовом классе. В приведенном коде есть одна тонкость: операторная функция
LocalPoolMP::operator=
всегда использует
new(pool)
вместо
new(lpmp.pool)
. Это повышает
Do'stlaringiz bilan baham: |