С++ - язык, который изучается постепенно.ГЛАВА 13. Простой список свободной памяти

Недорогой но высококачественный сайт. Такое может быть? Да. У нас может быть всё. Достойное качество по доступной цене.
С точки зрения нашей студии создание сайта недорого значит, прежде всего, отменно, технологично и потом уже - недорого.
Удаленная форма работы с клиентами оптимизирует наши расходы и мы можем делать сайты по всему миру. Вам совсем не нужно приезжать к нам. Мы сэкономим Ваше время и средства.

В столь непростое время глобального финансового кризиса, когда отмирают старые схемы бизнеса, появляются новые. Самое лучше время для начала своей деятельности. Вы начинаете свой бизнес, а я помогу создать вам свой сайт очень недорого, для вас.
Огромной популярностью пользуются так называемые сайты-визитки.
Создание сайта-визитки - это совсем недорого, и будет по карману даже начинающему предпринимателю. При разработке подобного сайта достаточно небольшого бюджета.


По последним данным, на рынке продается по крайней мере 2 768 942 книги о С++, не говоря уже о всевозможных курсах, обучающих программах, журналах и семинарах с коктейлями. И все же в этом изобилии наблюдается удручающее однообразие. Просматривать полку книг о С++ в книжном магазине ничуть не интереснее, чем литературу по бухгалтерии. В сущности, все книги пересказывают одно и то же и отличаются разве что по весу и количеству цветов в диаграммах и таблицах.На сегодняшний день язык Си и языки основанные на синтаксисе Си (например, C++, Java, C#) наиболее популярны в практическом программировании. Язык Си имеет массу достоинств, он прост в изучении и лаконичен. Элементы языка Си (массивы, функции, указатели) максимально приближены к архитектуре компьютеров. Студия Web-дизайна, создание, раскрутка сайта

                    



Материалы книги получены с http://www.itlibitum.ru/

Простой список свободной памяти

Рассмотрим простой пример. Оператор delete включает освобождаемые блоки в список свободной памяти. Оператор new сначала пытается выделить блок из списка и обращается к глобальному оператору new лишь в том случае, если список свободной памяти пуст.

class Foo {

private:

struct FreeNode {

FreeNode* next;

};

static FreeNode* fdFreeList;

public:

void* operator new(size_t bytes)

{

if (fgFreeList == NULL)

return ::operator new(bytes);

FreeNode* node = fgFreeList;

FgFreeList = fgFreeList->next;

return node;

}

void operator delete(void* space)

{

((FreeNode*)space->next = fgFreeList;

fgFreeList = (FreeNode*)space;

}

};

Как вы вскоре убедитесь, приведенный фрагмент неполон, однако он демонстрирует общие принципы перегрузки операторов new и delete для конкретного класса. Оператор new получает один аргумент, объем выделяемого блока, и возвращает адрес выделенного блока. Аргументом оператора delete является адрес освобождаемой области. Не пытайтесь объявлять их виртуальными; компилятор лишь посмеется над вами. При вызове оператора new компилятор точно знает, с каким классом он имеет дело, поэтому v-таблица ему не нужна. При вызове оператора delete деструктор определяет, какому классу этот оператор должен принадлежать. Если вы хотите гарантировать, что будет вызываться оператор delete производного класса, то виртуальным нужно сделать деструктор, а не оператор delete. Перегрузки будут унаследованы производными класса Foo, поэтому это повлияет и на

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

class Bar : public Baseclass, public Foo { ... };

Здесь Bar наследует все базовые характеристики типа Baseclass, а нестандартное управление памятью - от Foo.

Ограничения минимального размера

Перед тем как хвататься за компилятор, необходимо привести в порядок показанный фрагмент. Во-первых, предполагается, что экземпляр Foo содержит по крайней мере не меньше байт, чем Foo::FreeNode*. Для классов вроде нашего, не имеющего переменных и виртуальных функций, этого гарантировать нельзя. Он будет иметь определенный размер (во многих компиляторах - два байта), чтобы объекты обладали уникальным адресом, но по количеству байт он может быть меньше указателя на FreeNode. Мы должны гарантировать, что размер Foo не меньше размера указателя - для этого нужно включить в него v-таблицу или хотя бы переменные, дополняющие его до размера указателя.

Производные классы с добавленными переменными

Другая проблема заключается в том, что приведенный фрагмент не работает с производными классами, в которых добавляются новые переменные. Рассмотрим следующую иерархию классов.

class Bar : public Foo {

private:

int x;

};

Каждый экземпляр Bar по крайней мере на пару байт больше, чем экземпляр Foo. Если удалить экземпляр Foo, а затем попытаться немедленно выделить память для экземпляра Barкараул!

Выделенный блок оказывается на ту же на пару байт короче. Возможное силовое решение - сделать оператор new достаточно умным для того, чтобы он перехватывал только попытки выделения правильного количества байт. Позднее будут рассмотрены и более изящные решения.

class Foo {

public:

void* operator new(size_t bytes)

{

if (bytes != sizeof(Foo) || fgFreeList == NULL)

return ::operator new(bytes);

FreeNode* node = fgFreeList;

FgFreeList = fgFreeList->next;

Return node;

}

};

Мы избавились лишь от проблем, связанных с выделением памяти. Процесс освобождения необходимо изменить в соответствии с этой стратегией. Альтернативная форма оператора delete имеет второй аргумент - количество освобождаемых байт. На первый взгляд кажется, что из затруднений появился изящный выход:

class Foo {

public:

void* operator new(size_t bytes); // См. Выше

void operator delete(void* space, size_t bytes)

{

if (bytes != sizeof(Foo))

::operator delete(space);

((FreeNode*)space)->next = fgFreeList;

fgFreeList = (FreeNode*)space;

}

};

Теперь в список будут заноситься только настоящие Foo и производные классы, совпадающие по размеру. Неплохо, но есть одна проблема. Как компилятор поведет себя в следующем фрагменте?

Foo* foo = new Bar;

delete foo; // Какой размер будет использован компилятором?

Bar больше Foo, поэтому Foo::operator new перепоручает работу глобальному оператору new. Но

когда подходит время освобождать память, компилятор все путает. Размер, передаваемый

Foo::operator delete, основан на догадке компилятора относительно настоящего типа, а эта

догадка может оказаться неверной. В данном случае мы сказали компилятору, что это Foo, а не Bar; компилятор ухмыляется и продолжает играть по нашим правилам. Чтобы справиться с затруднениями, необходимо знать точную последовательность уничтожения, возникающую в операторах вида delete foo;. Сначала вызываются деструкторы, начиная с производного класса, и далее вверх по цепочке.

Затем оператор delete вызывается кодом, окружающим деструктор производного класса. Это

означает, что проблема возникает только для невиртуальных деструкторов. Если деструктор является виртуальным, аргумент размера в операторе delete всегда будет правильным - 2438-й довод в пользу применения виртуальных деструкторов, если только у вас не находится действительно веских причин против них.

Рабочий класс списка свободной памяти

Учитывая все сказанное, следующий фрагмент всегда будет правильно работать на компиляторах, использующих v-таблицы.

class Foo {

private:

struct FreeNode {

FreeNode* next;

};

static FreeNode* fdFreeList;

public:

virtual ~Foo() {}

void* operator new(size_t bytes)

{

if (bytes != sizeof(Foo) || fgFreeList == NULL)

return ::operator new(bytes);

FreeNode* node = fgFreeList;

FgFreeList = fgFreeList->next;

return node;

}

void operator delete(void* space, size_t bytes)

{

if (bytes != sizeof(Foo))

return ::operator delete(space);

((FreeNode*)space)->next = fgFreeList;

fgFreeList = (FreeNode*)space;

}

};

Указатель v-таблицы гарантирует, что каждый Foo по крайней мере не меньше указателя на

следующий элемент списка (FreeNode*), а виртуальный деструктор обеспечивает правильность размера, передаваемого оператору delete.

Повторяю: рассмотренная схема управления памятью не предназначена для практического применения (встретив производный класс, она собирает вещи и отправляется домой). Она лишь демонстрирует некоторые базовые принципы перегрузки операторов new и delete.


Назад    Содержание    Далее    

   Почти всегда целью создания сайта является получение прибыли, которая в свою очередь, зависит от его внешнего вида. Статистика говорит, что около 94% людей, при выборе товара, сначала обращают внимание на упаковку, а потом уже на её содержимое. И если эта упаковка не привлекательная и безвкусная, мало кто обратит на нее внимание, и, соответственно, товар не будет пользоваться спросом.
   В случае с интернет, “упаковкой” выступает ваш сайт, а “товаром” - его контент. Если сайт выглядит непривлекательно, то каким бы ценным и нужным не было его содержимое, люди будут обходить его стороной. Наша задача - сделать ваш сайт привлекательным и удобным, чтобы люди чувствовали себя уютно и комфортно, чтоб они возвращались к вам еще и еще. Соответствие между ценой и качеством вас, несомненно, порадуют.
.
   Мы делаем сайты для бизнеса, а не красочную картинку, которая увешена тяжеловесными флэшами и огромными фотографиями.
   Пользователя, когда он попадает на абсолютно любой сайт, прежде всего интересует информация, затем, как реализовать на этом сайте полученную информацию, чтобы было удобно и просто (юзабилити), подбор цветовой гаммы, расположение блоков на странице и многое другое.

   Перед тем, как заказывать создание сайта, рекомендуем прочесть статью А зачем мне (нам) сайт? или Что нужно знать заказчику сайта
Да и вообще, обратите внимание на раздел Статьи о продвижении сайта и бизнеса там вы найдёте ответы на многие вопросы.