Ответ
Кастомные аллокаторы в C++ позволяют переопределить стратегию выделения памяти для стандартных контейнеров. Это полезно для оптимизации производительности, например, при частых выделениях мелких объектов одного типа.
Пример: аллокатор на основе пула памяти для std::vector
#include <memory>
#include <vector>
#include <cstdlib>
template <typename T>
class PoolAllocator {
private:
struct Block {
Block* next;
};
Block* freeList = nullptr;
static const size_t POOL_SIZE = 1024;
void allocatePool() {
char* rawMemory = static_cast<char*>(std::malloc(POOL_SIZE * sizeof(T)));
for (size_t i = 0; i < POOL_SIZE; ++i) {
Block* block = reinterpret_cast<Block*>(rawMemory + i * sizeof(T));
block->next = freeList;
freeList = block;
}
}
public:
using value_type = T;
PoolAllocator() {
allocatePool();
}
template <typename U>
PoolAllocator(const PoolAllocator<U>&) {}
T* allocate(std::size_t n) {
if (n != 1 || freeList == nullptr) {
// Fallback для нестандартных запросов
return static_cast<T*>(std::malloc(n * sizeof(T)));
}
Block* block = freeList;
freeList = freeList->next;
return reinterpret_cast<T*>(block);
}
void deallocate(T* p, std::size_t n) {
if (n == 1) {
Block* block = reinterpret_cast<Block*>(p);
block->next = freeList;
freeList = block;
} else {
std::free(p);
}
}
};
int main() {
// Использование аллокатора с вектором
std::vector<int, PoolAllocator<int>> vec;
for (int i = 0; i < 100; ++i) {
vec.push_back(i); // Выделения происходят из предварительно созданного пула
}
return 0;
}
Когда это нужно:
- Снижение фрагментации: Все объекты одного типа выделяются в непрерывном регионе.
- Повышение локальности данных: Объекты, используемые вместе, находятся рядом в памяти.
- Оптимизация производительности: Избегание частых вызовов системного
malloc/new. - Специализированные хранилища: Работа с разделяемой памятью (shared memory) или памятью GPU.