Как понятно из названия, в данной статье речь пойдет о том, как создать объект в выделенной памяти и зачем это может быть необходимо.
Работа с памятью через оператор new может быть крайне не желательна в больших системах. Особенно на платформах, где ограниченный размер памяти, например при написание консольных игр. Выделение памяти в куче приводит к ее фрагментации, и через определенное время может не оказаться достаточного свободного блока памяти под новое выделение, что приведет к падению приложения.
Чтобы избежать подобных проблем часто делается свой собственный менеджер памяти, который ведет учет и контролирует размещение элементов в памяти. Например подобный менеджер может указать на утечки памяти.
Однако, в простейших случаях создание такого менеджера - избыточно. Есть более простой способ организовать управление памятью, например для какой-то конкретной системы.
Работа с памятью
Допустим есть какая то система, которая во время своей работы может создавать произвольное число объектов разных типов (возможно POD типов). Разумно ограничить полный размер памяти доступный для данной системы, тем самым гарантируя, что для данной системе не понадобится больше памяти. Всю необходимую память можно выделить при инициализации системы. Или выделять большими кусками, по мере расходования предыдущих блоков.
Кроме того, создание объекта в заранее выделенной памяти занимает значительно меньше времени, чем создание объекты с выделением памяти под него.
Блок памяти можно выделять как статически, так и динамически:
1
2
conat int bufferSize = 1024 * 1024; // указываем размер блока в 1 Мб.
unsigned char dataBuffer[ bufferSize ]; // заранее выделенный блок памяти
Когда появляется необходимость создать в этой памяти какой то объект, то его можно создать следующим образом:
1
2
3
4
5
void* ptr = dataBuffer[ offset ]; // указываем позицию в блоке памяти, где мы хотим создать объект.
SomeClass* cl = new (ptr) SomeClass(); // создаем объект в указанной памяти
Если написать такой код:
SomeClass* cl = new SomeClass(); // то будет выделение памяти в куче, а после создание в ней объекта.
Практическое применение
При создании игр часто ограничивают размер, который может занимать один игровой уровень со всеми объектами, звуками и т.п. После конца уровня, этот участок не освобождается, а новый уровень начинает грузиться в эту же память. Так можно гарантировать, что после игры в течение многих часов не будет проблем с памятью.
Это можно достичь используя следующий менеджер памяти (это только макет иллюстрирующий саму идею):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class MemPool
{
public:
MemPool( int size );
~MemPool();
void* Allocate( size_t size );
void FreeAll();
private:
void* _pool;
int _offset;
int _size;
};
MemPool::MemPool( int size )
{
_pool = new char[ size ];
_size = size;
_offset = 0;
}
MemPool::~MemPool()
{
delete( _pool );
}
void* MemPool::Allocate( size_t size )
{
if( _offset + size > _size )
return NULL;
void* ptr = _pool[ _offset ];
_offset += size;
return ptr;
}
void MemPool::FreeAll()
{
_offset = 0;
}
Работа с таким менеджером будет проходить следующим образом:
- создание менеджера памяти при старте программы
- при загрузке нового уровня все объекты используют заранее заготовленную память. Тут важно то, что если любой системе требуется память под что-либо, то она так же может брать память их этого хранилища.
- в конце уровня менеджер очищает память и при старте следующего уровня будет использоваться та же область памяти. Данная операция выполняется крайне быстро, т.к. память никак не модифицируется.
- удаление менеджера при выходе
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// создание менеджера памяти при старте программы
MemPool* globalPool = new MemPool( size );
// создание объектов в памяти менеджера
void* ptr = globalPool->Allocate( sizeof(SomeClass) );
SomeClass* cl = new (ptr) SomeClass();
// получение памяти под массив чисел
int arrSize = 10; // размер массива
int* intArr = globalPool->Allocate( sizeof(int) * arrSize );
// очистка используемой памяти
globalPool->FreeAll();
// удаление менеджера при выходе
delete( globalPool );