自定义内存分配器通过实现allocate/deallocate机制优化内存管理,适用于高频小对象分配场景。需定义value_type、pointer等类型及allocate(n)和deallocate(p, n)函数,支持rebind以适配不同类型。C++17起construct/destroy非必需,由std::allocator_traits统一处理。示例包括基于malloc的简单分配器和内存池分配器:后者预分配大块内存,维护空闲链表提升分配效率,适用于固定大小对象。使用时注意状态传递、线程安全与内存互操作性,避免全局重载new/delete,优先用于特定容器如std::vector以提升性能。

自定义内存分配器在C++中是一种提升性能或控制内存行为的常用手段,尤其是在处理大量小对象、需要内存池、或者对内存布局有特殊要求的场景下。标准库中的容器(如std::vector、std::list等)都支持通过模板参数传入自定义allocator。要实现一个自定义分配器,你需要遵循Allocator概念的要求。
理解标准Allocator接口
C++标准中的Allocator是一个类模板,需提供一些特定的类型定义和成员函数。最基本的接口包括:
- value_type:被分配类型的别名
-
pointer:指向
value_type的指针类型 - const_pointer:常量指针类型
- reference:引用类型
- const_reference:常量引用类型
- size_type:无符号整数类型,表示大小
- difference_type:有符号整数类型,表示指针差值
-
allocate(n):分配n个
value_type大小的内存块,不构造对象 - deallocate(p, n):释放从p开始的n个对象所占内存,不析构
- construct(ptr, args...):在指定地址构造对象(C++17前)
- destroy(ptr):显式调用析构函数(C++17前)
从C++17开始,construct和destroy不再是必须的,因为标准库改用std::allocator_traits来统一管理构造与析构。
实现一个简单的自定义分配器
下面是一个基于malloc和free的简单分配器示例,可用于替代std::allocator:
立即学习“C++免费学习笔记(深入)”;
#include#include template
struct MyAllocator { using value_type = T; using pointer = T; using const_pointer = const T; using reference = T&; using const_reference = const T&; using size_type = std::size_t; using difference_type = std::ptrdiff_t; // 支持不同类型的再绑定 templatezuojiankuohaophpcntypename Uyoujiankuohaophpcn struct rebind { using other = MyAllocatorzuojiankuohaophpcnUyoujiankuohaophpcn; }; MyAllocator() = default; templatezuojiankuohaophpcntypename Uyoujiankuohaophpcn MyAllocator(const MyAllocatorzuojiankuohaophpcnUyoujiankuohaophpcn&) {} // 分配原始内存 pointer allocate(size_type n) { if (n == 0) return nullptr; void* ptr = std::malloc(n * sizeof(T)); if (!ptr) throw std::bad_alloc(); return static_castzuojiankuohaophpcnpointeryoujiankuohaophpcn(ptr); } // 释放内存 void deallocate(pointer p, size_type) { if (p) std::free(p); }};
这个分配器可以用于std::vector:
std::vector> vec; vec.push_back(1); vec.push_back(2);
高级自定义:内存池分配器
若想进一步优化性能,可实现一个内存池分配器,避免频繁调用系统malloc/free。基本思路是预先分配一大块内存,然后从中切分小块返回。
关键点:
- 维护一个空闲链表(free list)
- 只在内存池耗尽时向系统申请新页
- 适用于固定大小对象的快速分配
简化版内存池分配器框架:
templateclass PoolAllocator { private: struct Block { Block* next; }; Block* free_list = nullptr; char* current_block = nullptr; size_t remaining = 0; void refill_pool() { current_block = new char[BlockSize]; size_t num_objects = BlockSize / sizeof(T); free_list = reinterpret_castzuojiankuohaophpcnBlock*youjiankuohaophpcn(current_block); for (size_t i = 0; i zuojiankuohaophpcn num_objects - 1; ++i) { reinterpret_castzuojiankuohaophpcnBlock*youjiankuohaophpcn(current_block + i * sizeof(T))-youjiankuohaophpcnnext = reinterpret_castzuojiankuohaophpcnBlock*youjiankuohaophpcn(current_block + (i+1) * sizeof(T)); } reinterpret_castzuojiankuohaophpcnBlock*youjiankuohaophpcn(current_block + (num_objects-1)*sizeof(T))-youjiankuohaophpcnnext = nullptr; remaining = num_objects; }public: using value_type = T; using pointer = T; using const_pointer = const T; using size_type = std::size_t;
templatezuojiankuohaophpcntypename Uyoujiankuohaophpcn struct rebind { using other = PoolAllocatorzuojiankuohaophpcnU, BlockSizeyoujiankuohaophpcn; }; PoolAllocator() = default; templatezuojiankuohaophpcntypename Uyoujiankuohaophpcn PoolAllocator(const PoolAllocatorzuojiankuohaophpcnU, BlockSizeyoujiankuohaophpcn&) {} pointer allocate(size_type n) { if (n != 1) throw std::bad_alloc(); // 只支持单个对象 if (remaining == 0) refill_pool(); Block* slot = free_list; free_list = free_list-youjiankuohaophpcnnext; --remaining; return reinterpret_castzuojiankuohaophpcnpointeryoujiankuohaophpcn(slot); } void deallocate(pointer p, size_type) { if (p) { Block* slot = reinterpret_castzuojiankuohaophpcnBlock*youjiankuohaophpcn(p); slot-youjiankuohaophpcnnext = free_list; free_list = slot; ++remaining; } }};
使用方式:
std::vector> fast_vec;
注意事项与最佳实践
自定义分配器虽然强大,但也有几点需要注意:
-
状态管理:如果分配器包含状态(如内存池指针),确保
rebind能正确传递状态 - 线程安全:默认情况下,分配器不保证线程安全,多线程使用时需加锁
- 兼容性:两个分配器实例应能互相释放对方分配的内存(即“可互操作”)
- 不要重载全局new/delete:除非必要,否则优先使用容器级分配器,避免影响整个程序
基本上就这些。自定义分配器的核心在于掌握allocate/deallocate机制,并根据需求优化内存获取策略。不复杂但容易忽略细节。










