C++固定大小内存池通过预分配连续内存块并用自由链表管理空闲块,实现O(1)分配/释放;需注意对齐、链表初始化及线程安全。

用C++实现一个简单的内存池,核心是预先分配一大块连续内存,避免频繁调用new/delete或malloc/free带来的系统开销和碎片问题。它适合对象大小固定、生命周期短且高频创建/销毁的场景(比如游戏中的粒子、网络包缓冲区)。
设计一个固定大小的内存池
最实用的入门方案是“单块固定尺寸”内存池:所有分配的内存块大小一致,管理简单、无碎片、速度极快。
- 启动时一次性申请一大块原始内存(如
std::vector<char></char>或new char[n]) - 用自由链表(free list)管理空闲块:每个空闲块头部存下一个空闲块的地址(即指针)
- 分配时取链表头,更新头指针;释放时把块地址插回链表头——都是O(1)操作
- 注意对齐:确保每块起始地址满足
alignof(std::max_align_t)或目标类型的对齐要求(可用std::align辅助)
关键代码结构示例
以下是一个轻量、无依赖的模板实现片段(省略异常处理和线程安全):
template <size_t BlockSize, size_t BlockCount = 1024>
class SimpleMemoryPool {
alignas(BlockSize) char buffer[BlockSize * BlockCount];
std::byte* free_list = nullptr;
<p>public:
SimpleMemoryPool() {
// 构建初始空闲链表:每个块头存下一个块地址
for (size_t i = 0; i < BlockCount - 1; ++i) {
auto<em> block = buffer + i </em> BlockSize;
*reinterpret_cast<std::byte*<em>>(block) = block + BlockSize;
}
free_list = buffer;
</em>reinterpret_cast<std::byte*<em>>(buffer + (BlockCount - 1) </em> BlockSize) = nullptr;
}</p><pre class="brush:php;toolbar:false;">void* allocate() {
if (!free_list) return nullptr;
void* ptr = free_list;
free_list = *reinterpret_cast<std::byte**>(free_list);
return ptr;
}
void deallocate(void* ptr) {
if (!ptr) return;
*reinterpret_cast<std::byte**>(ptr) = free_list;
free_list = static_cast<std::byte*>(ptr);
}};
立即学习“C++免费学习笔记(深入)”;
使用时:SimpleMemoryPool pool; → 分配64字节块,最多1024个。
配合自定义operator new和delete
让类直接使用该池,可重载其成员函数:
struct Particle {
float x, y, life;
static SimpleMemoryPool<sizeof(Particle)> pool;
<pre class="brush:php;toolbar:false;">void* operator new(size_t) { return pool.allocate(); }
void operator delete(void* p) { pool.deallocate(p); }};
立即学习“C++免费学习笔记(深入)”;
这样new Particle就走池子,delete p也自动归还,对业务代码透明。
注意事项与进阶方向
这个简单池不处理多线程竞争,也不支持变长分配。实际项目中需考虑:
- 加锁(如
std::mutex)或用无锁链表(atomic操作)支持并发 - 多个池按不同尺寸分级(如8/16/32/64/128字节),降低内部碎片
- 定期统计使用率,避免长期占用不释放(尤其在长生命周期服务中)
- 调试时可加入标记位、边界检查、释放后写毒值(poisoning)辅助排查use-after-free
不复杂但容易忽略对齐和链表初始化细节,写完建议用ASan或Valgrind验证内存行为。











