C++ 固定容量环形队列实现

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 本质是"固定容量对象池 + 环形队列",

在实时系统中通常比通用动态队列更可控、更稳定。

相关推荐
Tisfy1 小时前
LeetCode 0678.有效的括号字符串:O(n)+O(1)一次遍历
算法·leetcode·字符串·题解·贪心·括号匹配
91刘仁德1 小时前
传输层协议深度拆解:端口寻址、UDP 报文与 TCP 可靠性机制全解析
linux·网络·c++·网络协议·tcp/ip·udp·c
小O的算法实验室1 小时前
IEEE TEVC,学习辅助的搜索路径重构进化算法
学习·算法·重构
Liuchang09112 小时前
洛谷 P2670 [NOIP 2015 普及组] 扫雷游戏
c++·算法·游戏
言乐62 小时前
Python体现逻辑回归和决策树的区别
开发语言·python·算法·决策树·逻辑回归
顶点多余2 小时前
C/C++常见面试题
开发语言·c++
HugoStudio_SWAN2 小时前
洛谷 B4575 [GESP202609 二级] 直角三角形——临场定义理解与边界质疑
c++·学习·程序人生·算法
(Charon)2 小时前
【C++面试】右值引用与移动语义:std::move到底做了什么
开发语言·c++
红豆诗人2 小时前
C++二叉搜索树:查找、插入和删除为什么这样写
数据结构·c++·算法
HZjiangzi2 小时前
深耕精密驱动二十六载,捷昌驱动助力机器人产业升级发展
人工智能·算法