如何基于内存池分配块实现数组共享指针以支持写时复制字符串类
解决方案
a) 标准库已有方案
C++20 起标准库的std::allocate_shared支持数组特化,可直接满足需求:
std::allocate_shared<T[]>(alloc, runtime_length)可以在运行时指定数组长度,且符合std::make_shared的内存布局规则:控制块和数组内存会被分配在同一块连续内存空间中。- 你可以直接将自定义的内存池分配器作为第一个参数传入,分配出的
std::shared_ptr<char[]>天然满足你的内存布局要求,不需要手动处理引用计数逻辑。
如果你的编译环境不支持C++20,标准库没有现成的适配组件,需要自行实现。
b) 自定义精简共享指针实现
你不需要完整实现通用共享指针,针对你的场景可以做极简化设计:
第一步:定义控制块结构
#include <cstddef> template<typename Alloc> struct ControlBlock { size_t ref_count = 1; // 仅当你的分配器是有状态类型时需要保留该成员,无状态分配器可省略节省空间 Alloc alloc; // 柔性数组成员,后续内存直接作为字符数组使用 char data[]; };
注意:需要保证sizeof(ControlBlock<Alloc>) + 最大允许字符串容量 <= 内存池块大小,分配前做断言校验即可。
第二步:自定义智能指针核心逻辑
你的智能指针类只需要存储一个指向ControlBlock的指针,核心操作实现如下:
- 构造逻辑:从内存池分配固定大小的块,用定位new初始化
ControlBlock实例,字符数组的内容可按需拷贝初始化。 - 拷贝构造/拷贝赋值:直接复制
ControlBlock指针,将引用计数加1即可。 - 写时复制触发逻辑:所有会修改字符串内容的接口(非const
operator[]、append、assign等)调用前,先判断引用计数是否大于1:- 如果大于1,用当前控制块内的分配器分配新的内存块,拷贝原有字符串内容到新块
- 将原控制块的引用计数减1,若减到0则调用分配器的
deallocate释放原内存 - 将当前指针指向新的控制块
- 析构逻辑:将引用计数减1,若减到0则调用分配器的
deallocate释放整块内存。
优化提示
如果你的分配器是无状态类型(所有实例行为完全一致,无成员变量),可以完全省略控制块中存储的分配器实例,需要调用分配器接口时直接默认构造一个临时实例即可,进一步压缩控制块占用的空间。
内容的提问来源于stack exchange,提问作者Patrick Wright
相关产品推荐
相关产品推荐

