1. 适用场景
这种写法适合高频数据流(如视频帧、传感器数据):
- 需要固定内存上限,避免队列无限增长
- 希望减少频繁
new/delete带来的抖动 - 允许拥塞时丢帧(例如"丢旧保新"以降低时延)
2. 核心思路
底层用固定长度 vector<unique_ptr<T>> 作为槽位池,再配合三个状态量:
head:下次入队写入位置(写指针)tail:下次出队读取位置(读指针)size:当前有效元素数量
"环形"靠取模实现:
idx = (idx + 1) % capacity
这样索引走到末尾会回到 0,不需要搬移元素。
3. 两种满队列策略
A. 满了丢新(Reject-New)
- 队列满时直接返回失败
- 不覆盖队列中已有数据
- 简单直观,但可能导致"新数据进不来"
B. 满了丢旧(Keep-Latest)
- 队列满时仍接收新数据
- 通过前移
tail丢弃最老元素 - 更适合实时场景,减少处理过期数据的概率
4. 简化示例(独立可编译)
cpp
#include <iostream>
#include <memory>
#include <vector>
struct Frame {
int id = -1;
};
class RingQueue {
public:
explicit RingQueue(size_t capacity) : buf_(capacity), capacity_(capacity) {
for (size_t i = 0; i < capacity_; ++i) {
buf_[i] = std::make_unique<Frame>(); // 预分配槽位对象
}
}
// 满了丢新
bool PushRejectNew(std::unique_ptr<Frame>& in) {
if (size_ >= capacity_) return false;
std::swap(buf_[head_], in); // O(1) 交换所有权
head_ = (head_ + 1) % capacity_;
++size_;
return true;
}
// 满了丢旧,保留最新
bool PushKeepLatest(std::unique_ptr<Frame>& in, bool* overwrote_oldest = nullptr) {
bool overwritten = false;
std::swap(buf_[head_], in);
head_ = (head_ + 1) % capacity_;
if (size_ >= capacity_) {
tail_ = (tail_ + 1) % capacity_; // 丢最老元素
overwritten = true;
} else {
++size_;
}
if (overwrote_oldest) *overwrote_oldest = overwritten;
return true;
}
bool Pop(std::unique_ptr<Frame>& out) {
if (size_ == 0) return false;
std::swap(buf_[tail_], out);
tail_ = (tail_ + 1) % capacity_;
--size_;
return true;
}
private:
std::vector<std::unique_ptr<Frame>> buf_;
size_t capacity_{0};
size_t head_{0};
size_t tail_{0};
size_t size_{0};
};
5. 为什么 swap 能提效
- 不做大对象拷贝,只交换
unique_ptr所有权 - 减少动态分配/释放次数,降低内存碎片风险
- 延迟抖动更小,实时性更稳定
6. 实战注意点
- 多线程场景必须加锁(或使用无锁结构)
- 队列容量过小会导致覆盖/丢帧频繁
- 建议监控:队列长度、覆盖次数、端到端时延
7. 一句话总结
vector + 环形索引 + swap 本质是"固定容量对象池 + 环形队列",
在实时系统中通常比通用动态队列更可控、更稳定。