Читаем Фундаментальные алгоритмы и структуры данных в Delphi полностью

Что в предыдущем абзаце понимается под понятием "свободный список"? Это общепринятая конструкция в программировании. у нас имеется набор некоторых элементов, которые "распределяются" и "освобождаются". При освобождении элемента он, скорее всего, в какой-то момент времени будет использоваться повторно поэтому вместо возвращения занимаемой элементом памяти поддерживается свободный список, т.е. список свободных элементов. Диспетчер кучи Delphi использует свободный список освобожденных блоков памяти различного размера. Многие базы данных поддерживают скрытый свободный список удаленных записей, которые можно использовать повторно. Массив записей, описанный в главе 2, использует цепочку удаленных записей, которая является ни чем иным, как другим названием свободного списка. При необходимости распределения памяти под элемент приложение обращается к свободному списку и выбирает один из свободных элементов.

Давайте разработаем диспетчер распределения узлов. Он будет содержать свободный список узлов, который вначале устанавливается равным nil, что означает, что список пуст. При распределении узла диспетчер будет просматривать свой свободный список. Если он пуст (равен nil), диспетчер распределяет большой блок памяти, обычно называемый страницей, при помощи диспетчера кучи Delphi. Затем станица разделяется на отдельные куски с размером, равным размеру узла, и куски записываются в свободный список. После этого узел извлекается из списка и передается пользователю. При освобождении узла он записывается в свободный список. Под "записью" здесь понимается вставка узла в начало списка, а под "извлечением" - считывание узла с начала списка.

Листинг 3.1. Класс TtdNodeManager


TtdNodeManager = class private

FNodeSize : cardinal;

FFreeList : pointer;

FNodesPerPage : cardinal;

FPageHead : pointer;

FPageSize : cardinal;

protected


procedure nmAllocNewPage;

public

constructor Create(aNodeSize : cardinal);

destructor Destroy; override;


function AllocNode : pointer;

procedure FreeNode(aNode : pointer);

end;


Как видно из приведенного кода, реализация интерфейса класса достаточно проста. Класс позволяет создавать и уничтожать экземпляры и распределять и освобождать узлы. Конструктор Create в качестве единственного входного параметра принимает размер узла, а затем на его основании вычисляет несколько значений: количество узлов на странице и размер страницы. Класс пытается распределять страницы размером 1024 байта. Но если размер узла настолько велик, что на одной такой странице может поместиться только один узел, размер страницы выбирается равным размеру узла. Для увеличения эффективности размер узла округляется до ближайшей границы 4 байт (фактически округление выполняется до ближайшего значения sizeof(pointer)).

Листинг 3.2. Конструктор TtdNodeManager.Create


constructor TtdNodeManager.Create(aNodeSize : cardinal);

begin

inherited Create;

{сохранить размер узла, округленный до ближайших 4 байт}

if (aNodeSize <= sizeof(pointer)) then

aNodeSize := sizeof(pointer) else

aNodeSize := ((aNodeSize + 3) shr 2) shl 2;

FNodeSize := aNodeSize;

{вычислить размер страницы (по умолчанию используется 1024 байта) и количество узлов на странице; если размер страницы по умолчанию недостаточен для размещения двух или большего количества узлов, выбрать размер страницы равным размеру узла}

FNodesPerPage := (PageSize - sizeof(pointer)) div aNodeSize;

if (FNodesPerPage > 1) then

FPageSize := PageSize

else begin

FNodesPerPage := 1;

FPagesize := aNodeSize + sizeof(pointer);

end;

end;


Код метода AllocNode очень прост. Если свободный список пуст, вызывается метод nmAllocNewPage, который распределяет новую страницу и вносит все ее узлы в свободный список. Если свободный список не пуст, из него выбирается первый узел (фактически с помощью процедуры удаления первого узла).

Листинг 3.3. Распределение памяти для узла в классе TtdNodeManager


function TtdNodeManager.AllocNode : pointer;

begin

{если свободный список пуст, распределить память для новой страницы; это приведет к заполнению свободного списка}

if (FFreeList = nil) then

nmAllocNewPage;

{вернуть первый элемент свободного списка}

Result := FFreeList;

FFreeList := PGenericNode(FFreeList)^.gnNext;

end;


Тип PGenericNode представляет собой запись с одним полем - полем для ссылки gnNext. Этот тип и преобразование типов в коде позволяет работать с узлами в списке свободных узлов на основании общего алгоритма - нечто похожее на тип TSimpleNode, который мы рассматривали ранее. Обратите внимание, конструктор гарантирует, что размер узлов, отслеживаемых диспетчером узлов, составляет, по крайней мере, 4 байта, т.е. размер указателя.

Следующий метод - FreeNode - ничуть не сложнее предыдущего. Он просто вставляет новый узел в начало свободного списка (используется код вставки перед первым узлом).

Листинг 3.4. Освобождение узла в классе TtdNodeManager


procedure TtdNodeManager.FreeNode(aNode : pointer);

begin

{вставить узел (если он не nil) в начало свободного списка}

if (aNode <> nil) then begin

PGenericNode(aNode)^.gnNext := FFreeList;

FFreeList := aNode;

end;

end;


Перейти на страницу:

Похожие книги

C++
C++

С++ – это универсальный язык программирования, задуманный так, чтобы сделать программирование более приятным для серьезного программиста. За исключением второстепенных деталей С++ является надмножеством языка программирования C. Помимо возможностей, которые дает C, С++ предоставляет гибкие и эффективные средства определения новых типов. Используя определения новых типов, точно отвечающих концепциям приложения, программист может разделять разрабатываемую программу на легко поддающиеся контролю части. Такой метод построения программ часто называют абстракцией данных. Информация о типах содержится в некоторых объектах типов, определенных пользователем. Такие объекты просты и надежны в использовании в тех ситуациях, когда их тип нельзя установить на стадии компиляции. Программирование с применением таких объектов часто называют объектно-ориентированным. При правильном использовании этот метод дает более короткие, проще понимаемые и легче контролируемые программы. Ключевым понятием С++ является класс. Класс – это тип, определяемый пользователем. Классы обеспечивают сокрытие данных, гарантированную инициализацию данных, неявное преобразование типов для типов, определенных пользователем, динамическое задание типа, контролируемое пользователем управление памятью и механизмы перегрузки операций. С++ предоставляет гораздо лучшие, чем в C, средства выражения модульности программы и проверки типов. В языке есть также усовершенствования, не связанные непосредственно с классами, включающие в себя символические константы, inline-подстановку функций, параметры функции по умолчанию, перегруженные имена функций, операции управления свободной памятью и ссылочный тип. В С++ сохранены возможности языка C по работе с основными объектами аппаратного обеспечения (биты, байты, слова, адреса и т.п.). Это позволяет весьма эффективно реализовывать типы, определяемые пользователем. С++ и его стандартные библиотеки спроектированы так, чтобы обеспечивать переносимость. Имеющаяся на текущий момент реализация языка будет идти в большинстве систем, поддерживающих C. Из С++ программ можно использовать C библиотеки, и с С++ можно использовать большую часть инструментальных средств, поддерживающих программирование на C. Эта книга предназначена главным образом для того, чтобы помочь серьезным программистам изучить язык и применять его в нетривиальных проектах. В ней дано полное описание С++, много примеров и еще больше фрагментов программ.

Мюррей Хилл , Бьёрн Страуструп , Бьярн Страустрап

Программирование, программы, базы данных / Программирование / Книги по IT