应手写数组循环列表而非std::deque,因其内存连续、无指针跳转、cache友好;需用模运算维护头尾索引,预留空位判满,避免无符号整型减法溢出;定长版本最轻量,clear时须显式销毁元素。

为什么不用 std::deque 而要手写数组循环列表
因为你要控制内存布局、避免指针间接跳转、或对接裸硬件/嵌入式环境;std::deque 是分段连续的,内部有指针跳转和额外元数据,而纯数组循环列表所有元素紧挨着,front() 和 back() 都是 O(1) 且 cache 友好。但代价是你得自己管好头尾索引和容量边界。
push_back 和 pop_front 的索引更新逻辑怎么写才不出错
核心是两个索引:m_head(指向第一个有效元素)、m_tail(指向下一个空位)。插入时更新 m_tail,删除时更新 m_head,全部用模运算绕回:
m_tail = (m_tail + 1) % m_capacity; m_head = (m_head + 1) % m_capacity;
注意三点:
- 初始化时
m_head == m_tail表示空,但满的时候也满足这个条件 → 必须预留一个空位,即实际可用容量为m_capacity - 1 - 不要在每次操作里都算
size() == capacity()来判断满,而是直接用(m_tail + 1) % m_capacity == m_head判断是否将溢出 - 如果用无符号整型(如
size_t)做索引,(m_head - 1) % m_capacity会因借位变极大值 → 改成(m_head + m_capacity - 1) % m_capacity
模板参数要不要支持自定义分配器
如果你只在栈上用或明确知道生命周期(比如作为类成员固定大小),直接用 T[m_capacity] 数组最轻量;加分配器会引入 std::allocator 依赖和构造/析构手动管理开销。但若需要运行时指定容量,就得用 new T[m_capacity] 并显式调用 std::construct_at 和 std::destroy_at —— C++17 起必须这么做,否则对非 trivial 类型(如 std::string)可能漏构造。
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
简单起见,先写定长版本,模板参数长这样:
template<typename T, size_t N>
class circular_array {
T m_data[N];
size_t m_head = 0;
size_t m_tail = 0;
static constexpr size_t m_capacity = N;
};
迭代器失效和线程安全怎么处理
这个容器不提供迭代器(除非你额外实现),所以不存在迭代器失效问题;但所有修改操作(push_back、pop_front、clear)都会让之前取到的引用/指针立即悬空 —— 因为元素位置随头尾滑动而变,&data()[i] 不等于“第 i 个插入的元素地址”。
线程安全完全不考虑:没加锁,也没标 mutable 或 std::atomic。如果多线程读写,必须由使用者在外层加互斥;只读可并发,但要注意 size() 这种非原子读可能看到中间态(比如 m_tail 已更新、m_head 还没更新完)。
最容易被忽略的是:清空后 m_head 和 m_tail 都重置为 0,但旧数据还在内存里 —— 如果存的是 std::unique_ptr,不显式销毁会导致资源泄漏;务必在 clear() 里遍历并调用 std::destroy_at。

















