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


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

Списки свободных блоков

Упрощенный список свободных блоков, приведенный в предыдущей главе, может использоваться только для объектов постоянного размера. Например, он не будет нормально работать с производными классами, в которых добавляются новые переменные, поскольку они будут иметь другой размер. Сам список тоже ограничивался одним размером; для передачи оператору delete правильного размера требовались виртуальные деструкторы. Впрочем, создать более универсальные списки уже не так уж

трудно.

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

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

Более эффективное представление - коллекция списков свободной памяти, в которой каждый список представляет блоки определенного размера. Ниже показана упрощенная, но полезная реализация.

class MemManager {

private:

struct FreeList { // Список с блоками определенного размера

FreeList* next; // Следующий FreeList

void* top_of_list; // Верх текущего списка

size_t chunk_size; // Размер каждого свободного блока

FreeList(FreeList* successor, size_t size) : next(successor),

top_of_list(NULL), chunk_size(size) {}

};

FreeList* all_lists; // Список всех FreeList

public:

MemManager() : all_lists(NULL) {}

void* Allocate(size_t bytes);

void Deallocate(void* space, size_t bytes);

};

void* MemManager::Allocate(size_t bytes)

{

for (FreeList* fl = all_lists;

fl != NULL && fl->chunk_size != bytes;

fl = fl->next)

{

if (fl->top_of_list != NULL)

{

void* space = fl->top_of_list;

fl->top_of_list = *((void**)(fl->top_of_list));

return space;

}

return ::operator new(bytes); // Пустой список

}

return ::operator new(bytes); // Такого списка нет

}

void MemManager::Deallocate(void* space, size_t bytes)

{

FreeList* fl = NULL;

for (fl = all_lists; fl != NULL; fl = fl->next)

if (fl->chunk_size == bytes) break;

if (fl == NULL) // Списка для такого размера нет

{

fl = new FreeList(all_lists, bytes);

all_lists = fl;

}

*((void**)space) = fl->top_of_list;

fl->top_of_list = space;

}

Функции Allocate() и Deallocate() вызываются из перегруженных операторов new и delete

соответственно. Такой подход предельно упрощен, но работает он неплохо. Вы можете

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

1.Ограничить размеры блоков числами, кратными некоторому числу байт, степенями 2 или числами Фибоначчи.

2.Воспользоваться более эффективной структурой данных, чем связанный список списков - возможно, бинарным деревом или даже массивом, если диапазон размеров невелик.

3.Предоставить функцию Flush(), которая при нехватке памяти удаляет все содержимое

списков.

4.В функции Allocate() при отсутствии в списке свободного места заданного размера выделить память под массив блоков этого размера вместо одного блока.


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

Copyright 2005. Климов Александр. All Right Reserved.
Сайт создан в системе uCoz