228
Невидимые ведущие указатели
Чтобы не использовать шаблоны дескрипторов и
ведущих указателей, можно было организовать
множественное наследование ведущих указателей от
VoidPtr
и гомоморфного базового класса. Иначе
говоря, ведущие указатели становятся невидимыми, как объяснялось в предыдущих главах. В
игру
вступают все механизмы, сопутствующие идиоме невидимых указателей (например, производящие
функции).
class Foo {
public:
static Foo* make();
// Возвращает пару указатель-указываемый объект
virtual void Member1() = 0;
//
И т.д. для открытого интерфейса
};
// В файле foo.cpp
class FooPtr :
public Foo, public VoidPtr {
public:
FooPtr(Foo* foo, size_t size) : VoidPtr(foo, size) {}
virtual ~FooPtr() { delete (Foo*)address; }
virtual void Member1()
{ ((Foo*)address)->Member1(); }
//
И т.д. для остальных открытых функций
};
class RealFoo : public Foo { ... };
Foo* Foo::make()
{
return new FooPtr(new RealFoo, sizeof(RealFoo));
}
// В клиентском коде
class Bar {
private:
Foo*
foo;
//
На самом деле невидимый указатель
public:
Bar() : foo(Foo::make()) {}
};
Такое решение улучшает инкапсуляцию применения ведущих указателей. Кроме того, оно позволяет
производящим функциям решить, какие экземпляры должны управляться подобным образом, а какие
— с помощью обычных невидимых указателей или даже вообще без указателей. Все прекрасно
работает, пока вы соблюдаете осторожность и выделяете в
VoidPtrPool
достаточно
места для
FooPtr
. Помните, что из-за множественного наследования по сравнению с
VoidPtr
размер
увеличивается, по крайней мере, на v-таблицу.
Объекты классов
Возможен и другой вариант — потребовать, чтобы все объекты происходили от общего класса-предка,
способного возвратить объект класса для данного объекта или, по
крайней мере, размер объекта. В
этом случае вам уже не придется хранить в указателе объект экземпляра, поскольку его можно будет
получить у объекта класса. Если вы готовы смириться с некоторым насилием в отношении типов в
дескрипторах, это также позволит вам избежать шаблонов второго уровня, используемых для главных
указателей. Вместо
void*
в
VoidPtr
можно будет хранить
CommonBase*
(где
CommonBase
— общий
базовый класс). Мы избавляемся от переменной size, от необходимости иметь шаблон, производный от
VoidPtr
, и от виртуального деструктора в
VoidPtr
, и как следствие — от 4-байтового
адреса v-
таблицы. С другой стороны, если управляемые объекты уже содержат v-таблицы и не принуждают вас
к применению множественного наследования, дополнительных затрат не будет.