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

1 ... 69 70 71 [ 72 ] 73 74 75 ... 82


Невидимые ведущие указатели

Чтобы не использовать шаблоны дескрипторов и ведущих указателей, можно было организовать множественное наследование ведущих указателей от 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-таблицы и не принуждают вас к применению множественного наследования, дополнительных затрат не будет.



Оптимизация в особых ситуациях

Если адрес переменной класса получать не требуется, ее можно хранить в виде внедренного объекта. Впрочем, как показывает следующий фрагмент, ситуация не всегда находится под контролем разработчика класса:

void f(int); class Foo { private:

int x; Адрес получать не нужно, поэтому храним непосредственно public:

void F() { f(x); }

Вышядит вполне безопасно, не правда ли? А теперь предположим, что автор функции f() привел ее интерфейс к следующему виду:

void f(int&);

И вот вся тщательно спроектированная оптимизация обрушивается вам на голову. У внедренных объектов есть еще одна проблема: вы должны проследить не только за тем, чтобы никогда не получать адрес объекта, но и за тем, чтобы никогда не получать адресов рекурсивно внедренных членов.

class Bar { private:

Foo foo;

Допустим, вы сможете доказать, что ни одна из функций Bar не получает адрес foo. Но вам придется сделать следующий шаг и проследить еще и за тем, чтобы все функции Foo тоже были безопасными. Та же логика относится и к базовым классам. Важно понимать, что такая оптимизация должна осуществляться на уровне всей программы, а не только проектируемого класса.

Алгоритм Бейкера

Один из алгоритмов уплотнения жертвует неимоверным количеством (а точнее, половиной) памяти в интересах скорости. Процесс уплотнения понемногу вплетается в обычную работу программы. Этот алгоритм называется алгоритмом Бейкера (Bakers Algorithm).

Пул памяти делится на две половины, A и B. В любой момент времени одна из этих половин является активной (то есть в ней создаются новые объекты). Память выделяется снизу вверх, а в момент удаления объекта не делается никаких попыток вернуть занимаемую им память. Время от времени все активные объекты копируются из одной половины памяти в другую. В процессе копирования автоматически происходит уплотнение нижней части новой активной половины. Активным называется объект, для которого в стане ведущих указателей найдется ссылающийся на него ведущий указатель (в нашем случае VoidPtr).

Пространства объектов

Половины представлены в виде пространств памяти для создания объектов. Класс HalfSpace изображает одну половину, а Space - всю память, видимую клиентам.

Класс HalfSpace

Каждая половина по отдельности выглядит как обычное пространство памяти со специализированной функцией Allocate (). Парная функция Dea11ocate() не понадобится.

class HalfSpace { private:

unsigned long next byte; Следующий выделяемый байт unsigned char bytes[HALFSIZE]; public:



Ha1fSpace() : next byte(0) {}

void* Al1ocate(size t size);

void Reinitia1ize() { next byte = 0; }

void* Ha1fSpace::Al1ocate(size t size)

Выровнять до границы слова

size = ROUNDUP(size);

if (next byte + size >= HALFSIZE)

return NULL; Не хватает памяти void* space = &bytes[next byte]; next byte += size; return space;

Класс Space

Общий пул представляет собой совокупность двух половин. Он также имеет функцию Al1ocate(), которая в обычных ситуациях просто поручает работу активной половине. Если в активной половине не найдется достаточно памяти, происходит переключение половин и копирование активных объектов в другую половину функцией Swap() . Эта схема основана на предыдущем материале - специализированном пуле VoidPtr со средствами перебора.

class Space { private:

HalfSpace A, B;

HalfSpace* active;

HalfSpace* inactive;

void Swap(); Переключить активную половину, скопировать объекты public:

Space() : active(&A), inactive(&B) {}; void* Al1ocate(size t size);

void* Space::Al1ocate(size t size)

void* space = active->Al1ocate(size); if (space != NULL) return space;

Swap();

Space = active->Al1ocate(size);

if (space == NULL)

Исключение - нехватка памяти return space;

void Space::Swap()

if (active == &A)

active = &B; inactive = &A;

else



1 ... 69 70 71 [ 72 ] 73 74 75 ... 82

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