分配器按值复制时如何共享状态?自定义区域分配器实践疑问
区域分配器共享状态的业界常用解决方案
你实现的栈上线性区域分配器在配合std::map使用时,遇到了分配器按值复制导致状态无法共享的问题——rebind生成的新分配器实例会复制原始状态,后续的分配操作只会修改副本的状态,原始实例完全感知不到变化。
业界解决这类问题的标准模式是将可变状态抽离到一个共享的堆分配对象中,让所有分配器实例通过智能指针共享这个状态。具体实现步骤如下:
1. 提取共享状态结构体
把分配器中需要共享的可变状态(当前内存偏移、内存起止地址)放到一个独立的结构体里,用std::shared_ptr来管理它的生命周期:
#include <memory> #include <cassert> struct RegionState { char* m_memBegin = nullptr; char* m_memCurr = nullptr; char* m_memEnd = nullptr; RegionState(void* baseAddress, size_t sizeInBytes) : m_memBegin(static_cast<char*>(baseAddress)), m_memCurr(m_memBegin), m_memEnd(m_memBegin + sizeInBytes) {} };
2. 修改分配器实现,持有共享状态指针
调整region_allocator,让它持有std::shared_ptr<RegionState>,所有复制/rebind操作都会共享同一个状态实例:
template <typename T> struct region_allocator { using value_type = T; // 禁用默认构造 region_allocator() = delete; // 从原始内存块构造,创建共享状态 region_allocator(void* baseAddress, size_t sizeInBytes) : m_state(std::make_shared<RegionState>(baseAddress, sizeInBytes)) {} // 跨类型复制构造(rebind时调用) template <class U> constexpr region_allocator(const region_allocator<U>& other) noexcept : m_state(other.m_state) {} // rebind模板,返回对应类型的分配器 template <class U> struct rebind { using other = region_allocator<U>; }; // 分配内存:操作共享状态中的偏移 T* allocate(std::size_t n) { char* address = m_state->m_memCurr; const size_t sizeRequired = sizeof(T) * n; m_state->m_memCurr += sizeRequired; if (m_state->m_memCurr > m_state->m_memEnd) [[unlikely]] { m_state->m_memCurr -= sizeRequired; assert("region_allocator out of memory"); } return static_cast<T*>(static_cast<void*>(address)); } // 释放操作:依然不做任何事,由用户统一释放整个内存块 void deallocate(T* p, std::size_t n) noexcept {} private: // 共享状态指针,所有分配器实例共享同一个状态 std::shared_ptr<RegionState> m_state; // 允许其他类型的region_allocator访问m_state template <class U> friend struct region_allocator; }; // 分配器相等性判断:只要共享同一个状态,就视为相等 template <class T, class U> bool operator==(const region_allocator<T>& lhs, const region_allocator<U>& rhs) noexcept { return lhs.m_state == rhs.m_state; } template <class T, class U> bool operator!=(const region_allocator<T>& lhs, const region_allocator<U>& rhs) noexcept { return !(lhs == rhs); }
关键说明
- 状态共享:不管是分配器的复制构造还是rebind生成新实例,都会复用同一个
RegionState指针,所有分配操作都会修改同一个状态,原始分配器实例能实时感知到偏移变化。 - 生命周期管理:
std::shared_ptr会自动管理RegionState的生命周期,只要还有分配器实例存在,状态就不会被销毁;但要注意栈内存块的生命周期必须长于所有分配器实例以及它们分配的对象,避免出现悬空指针。 - 多线程注意:如果你的场景是多线程分配,需要给
RegionState中的m_memCurr加上同步保护(比如用std::atomic<char*>或者std::mutex),因为当前的allocate操作不是线程安全的。
内容的提问来源于stack exchange,提问作者Steven
相关产品推荐
相关产品推荐

