标准分配器依赖进程私有堆,无法用于嵌入式或共享内存场景;自定义分配器需实现allocate/deallocate、类型别名及is_always_equal特化,且分配器类型决定容器类型。

为什么标准分配器不能直接用在嵌入式或共享内存场景
标准 std::allocator 底层调用 ::operator new 和 ::operator delete,本质依赖堆(heap)和进程私有内存空间。在嵌入式系统中可能没有完整堆管理;在共享内存中,new 分配的地址对其他进程不可见——这时候必须自己接管内存来源。
自定义分配器不是为了“炫技”,而是为了控制:内存从哪来、怎么对齐、是否线程安全、构造/析构是否跳过(比如 placement-new 场景)。
- 必须实现
allocate()和deallocate(),这是核心入口 - 必须定义
value_type、pointer、const_pointer等类型别名,否则容器模板推导失败 - 推荐显式特化
is_always_equal:若分配器实例间可互换(比如都指向同一块共享内存段),设为std::true_type,否则容器可能做冗余状态拷贝
如何让 vector 正确调用 placement-new 构造对象
分配器只管内存,不负责构造。C++11 起,std::allocator 通过 construct() 和 destroy() 两个函数桥接——但这两个函数**已被弃用(C++17)**,现代写法应依赖 std::allocator_traits 的默认行为:它会自动用 ::new (ptr) T(std::forward<args>(args)...)</args> 完成就位构造。
所以你只需确保:allocate() 返回的指针满足 T 的对齐要求(用 alignof(T) 检查),且 deallocate() 能正确释放对应内存块。
- 不要重写
construct(),除非你要拦截构造参数(如日志、对象池计数) - 若真要重写,签名必须是
void construct(pointer p, Args&&... args),内部用std::construct_at(p, std::forward<args>(args)...)</args>(C++20)或手动 placement-new - 避免在
deallocate()里调用destroy():容器会在析构元素后才调用deallocate()
shared_memory_allocator 示例:基于 mmap 的跨进程分配器
假设你有一块已映射的共享内存区域 void* base 和大小 size_t len,需要支持多线程并发分配(但不保证回收后立即复用):
template<typename t>
struct shared_memory_allocator {
using value_type = T;
using pointer = T*;
using const_pointer = const T*;
using size_type = std::size_t;
<pre class="brush:php;toolbar:false;">void* base_;
size_type offset_ = 0;
mutable std::mutex mtx_;
shared_memory_allocator(void* b) : base_(b) {}
pointer allocate(size_type n) {
const size_type bytes = n * sizeof(T);
const size_type aligned_offset = (offset_ + alignof(T) - 1) & ~(alignof(T) - 1);
std::lock_guard lock(mtx_);
if (aligned_offset + bytes > static_cast<size_type>(/* total size */))
throw std::bad_alloc{};
pointer p = static_cast<pointer>(static_cast<char*>(base_) + aligned_offset);
offset_ = aligned_offset + bytes;
return p;
}
void deallocate(pointer p, size_type n) noexcept {
// 共享内存不回收,只留作示意
}
};
注意:这个分配器是“仅增长”型,适合初始化阶段批量构建对象;真实共享内存分配器需配合 slab 或 buddy 系统,且 deallocate() 必须能识别所属 block。
为什么 operator== 重载几乎总是多余的
除非你的分配器携带运行期状态(如指向不同内存池的句柄),否则两个同类型分配器实例应该等价。C++ 标准规定:若 std::allocator_traits<a>::is_always_equal::value</a> 为 true,容器将跳过分配器比较;否则必须提供 operator== 和 operator!=。
- 99% 的自定义分配器应加上
using is_always_equal = std::true_type; - 如果分配器持有
std::shared_ptr<memory_pool></memory_pool>这类资源,则需实现operator==比较底层指针 - 不要在
operator==里做深比较(比如检查内存池当前使用量):标准只要求“是否能互换使用”
真正容易被忽略的是:分配器类型一旦用于容器模板参数,就成为类型的一部分——vector<int a1></int> 和 vector<int a2></int> 是完全不同的类型,无法赋值或传递,哪怕两个分配器逻辑一模一样。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











