allocator 是 std::allocator 模板,封装原始内存分配/释放与对象构造/析构;它不是内存池或智能指针替代品,标准容器默认使用但用户极少直接调用其成员。

allocator 是什么,不是什么
它不是内存池,也不是智能指针的替代品;它是 std::allocator 模板,负责封装「如何分配/释放原始内存」和「如何构造/析构对象」这两件事。标准容器(如 std::vector、std::map)默认用它来管理元素内存,但你几乎从不直接调用它的 allocate() 或 construct() —— 容器内部在用。
写一个最简可用的自定义 allocator
必须实现至少 4 个关键成员:类型别名、allocate()、deallocate()、construct() 和 destroy()(C++17 起 construct()/destroy() 可被省略,但建议显式提供)。下面是最小可行骨架:
template<typename T>
struct MyAlloc {
using value_type = T;
using pointer = T*;
using const_pointer = const T*;
<pre class="brush:php;toolbar:false;">T* allocate(std::size_t n) {
return static_cast<T*>(::operator new(n * sizeof(T)));
}
void deallocate(T* p, std::size_t) {
::operator delete(p);
}
template<typename U, typename... Args>
void construct(U* p, Args&&... args) {
::new (static_cast<void*>(p)) U(std::forward<Args>(args)...);
}
template<typename U>
void destroy(U* p) { p->~U(); }};
- 别漏掉
value_type—— 容器靠它推导元素类型,缺了编译不过 -
allocate()返回的是未构造的原始内存,不能直接返回new T[n](那会触发构造) -
deallocate()的第二个参数是n,但标准库实际传入的值可能和allocate()的n不一致(比如容器预留空间时),所以通常忽略它 - C++20 起推荐继承
std::allocator_traits<myalloc>></myalloc>来补全缺失接口,但新手起步时手写这五个就够了
怎么让 vector 用上你的 allocator
不是全局替换,而是模板实参传入。所有标准容器都支持第二个模板参数指定 allocator:
立即学习“C++免费学习笔记(深入)”;
std::vector<int, MyAlloc<int>> v; v.push_back(42); // 这时才真正调用 MyAlloc::allocate() 和 construct()
- 注意:
MyAlloc<int></int>和MyAlloc<double></double>是完全不同的类型,allocator 是类型绑定的,不能混用 - 如果容器已有数据(比如从默认 allocator 构造的 vector),不能“切换”allocator —— 必须一开始就指定
- 不要试图把
MyAlloc<int></int>强转成std::allocator<int></int>,它们没有继承关系,也不兼容 - 调试时可在
allocate()里加std::cout 验证是否生效
踩坑最多的地方:rebind 和 propagate_on_container_copy_assignment
当容器内部需要为节点或临时缓冲区分配其他类型(比如 std::list<t></t> 分配 node<t></t>),它会通过 allocator_traits<a>::rebind_alloc<u></u></a> 获取对应类型的 allocator。如果你没提供 rebind,编译器会尝试用嵌套类或别名推导 —— 很容易失败。
- 最稳妥做法:加一个
template<typename u> struct rebind { using other = MyAlloc<u>; };</u></typename> - 另一个隐形雷:
propagate_on_container_copy_assignment控制拷贝容器时是否连 allocator 一起拷贝。默认是std::false_type,意味着两个容器即使用了同种 allocator 类型,其内部 allocator 实例也可能不等价 —— 如果你依赖 allocator 实例状态(比如计数器、内存池句柄),必须设为std::true_type并重载赋值操作符 - 别手动特化
std::allocator_traits—— 它是为通用性设计的,你的 allocator 应该适配它,而不是反过来
真正难的从来不是写 allocate/deallocate,而是让 allocator 在各种容器、各种操作(移动、拷贝、swap)、各种 C++ 标准版本下行为一致。哪怕只在一个 vector 上跑通,也不代表它能在 list 或 unordered_map 里安全工作。









