C++ 实现环形缓冲区

环形缓冲区(Ring Buffer)是一种常见的用于数据流缓冲的结构,通常用于生产者-消费者模型、音视频处理等场景。

因为环形缓冲区使用的场景大多为性能敏感的场景,我们采用数组的数据结构和位运算来实现,以提高代码效率。位运算的效率要高于模运算,但是用位运算替代模运算的前提是缓冲区的大小必须为 2 的整数次幂,因为对于 2 的幂来说,模运算就是屏蔽高位,这个在下面展示代码的时候细说。

因为要适配不同的类型,在头文件中使用模板,由于模板类和模板函数是在使用时才实例化的,编译器需要在包含模板的地方就能看到其完整实现,如果编译器看不到它的实现,在链接时就会报错(undefined reference),通常不能将模板的实现写在.cpp文件中。但是也不推荐把模板类的声明和定义全写在.h文件里,推荐的方式是.h + .tpp的方式,这样可以分离接口与实现,提高可读性,也可以避免不必要的重复编译——如果都写在.h文件中,每次这个头文件被#include,就会重新编译一遍模板定义,编译时间会变长。

环形缓冲区类的头文件 RingBuffer.h

#ifndef RINGBUFFER_H
#define RINGBUFFER_Htemplate<typename T, size_t Capacity>
class RingBuffer {static_assert((Capacity & (Capacity - 1)) == 0, "Capacity must be a power of 2");public:RingBuffer();bool push(const T& item);bool pop(T& item);bool empty() const;bool full() const;size_t size() const;void reset();private:T buffer_[Capacity];size_t head_;size_t tail_;bool full_;
};#include "RingBuffer.tpp"#endif

环形缓冲区类的实现部分RingBuffer.tpp

#ifndef RINGBUFFER_TPP
#define RINGBUFFER_TPPtemplate<typename T, size_t Capacity>
RingBuffer<T, Capacity>::RingBuffer(): head_(0),tail_(0),full_(false) {}template<typename T, size_t Capacity>
bool RingBuffer<T, Capacity>::push(const T &item) {if (full_) return false;buffer_[head_] = item;head_ = (head_ + 1) & (Capacity - 1);if (head_ == tail_) full_ = true;return true;
}template<typename T, size_t Capacity>
bool RingBuffer<T, Capacity>::pop(T &item) {if (empty()) return false;item = buffer_[tail_];tail_ = (tail_ + 1) & (Capacity - 1);full_ = false;return true;
}template<typename T, size_t Capacity>
bool RingBuffer<T, Capacity>::empty() const {return (!full_ && (head_ == tail_));
}template<typename T, size_t Capacity>
bool RingBuffer<T, Capacity>::full() const {return full_;
}template<typename T, size_t Capacity>
size_t RingBuffer<T, Capacity>::size() const {if (full_) return Capacity;if (head_ >= tail_) return head_ - tail_;return head_ + Capacity - tail_;
}template<typename T, size_t Capacity>
void RingBuffer<T, Capacity>::reset() {head_ = tail_ = 0;full_ = false;
}#endif

主函数代码:

#include <iostream>
#include "RingBuffer.h"using namespace std;int main() {RingBuffer<int, 8> buffer;for (int i = 0; i < 7; ++i) {if (buffer.push(i))cout << "Pushed: " << i << "\n";elsecout << "Buffer full, cannot push: " << i << "\n";}int val;while (buffer.pop(val)) {cout << "Popped: " << val << "\n";cout << buffer.size() << endl;}return 0;
}

如何判断一个数是 2 的幂?可以通过:

(Capacity & (Capacity - 1)) == 0

因为 2 的幂都是形如 1000 这样的数字,减一后除了首位外全为 1,利用&的位运算之后全为 0。

如何用位运算替代模运算?就是利用位运算屏蔽高位。例如用 a & (b - 1) 替代 a % b,前提 b 是 2 的整数次幂。例如 1111 % 1000,就是把高于 000 的位数全部去掉,因此可以利用 1000 - 1 = 0111 的高位 0 来“与”掉所有的高位,因为 0 与任何数还是 0 ,1 与任何数还是数本身。例如:

15 % 8 == 7
0b1111 & 0b0111 = 0b0111

注意这里不能用移位操作( >> 或者 << ),左移和右移操作替代的是除法和乘法:

x / 2^n   →   x >> n
x * 2^n   →   x << n

环形缓冲区的头尾初始值都是 0,符合条件时进行 push 和 pop 操作时,head_ 和 tail_ 的值都后移。

每次执行 push 操作,先检测一下缓冲区是否是满的,如果不满就将数据插入头位置,然后把头位置后移一位,如果 head_ + 1 大于 Capacity,则触发一次回绕,通过求余(通过模运算或者位运算)得到新的 head_。除了初始状态head_ = tail_ = 0,full_ = false,后续的操作中,如果head_ tail_的值相同,则判定现在缓冲区已满(因为 head_ == tail_ 时既可能是空也可能是满,必须通过 full_ 标志来区分)。

size() 函数在处理 head_ < tail_ 的情况时(这种情况出现在多次 push 操作使得回绕被触发,且 pop 的操作次数少于 push 操作的时候),计算缓冲区中的数据量时需要用 Capacity 减去 head_ 和 tail_ 的差值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/news/909084.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL虚拟列:一个被低估的MySQL特性

前言 最近在做订单系统重构时&#xff0c;遇到了一个有趣的问题。 系统里有很多地方都要计算订单的总价&#xff08;数量单价&#xff09;&#xff0c;这个计算逻辑分散在各个服务中&#xff0c;产生了不少相似甚至重复的代码。 代码评审时&#xff0c;同事提出了一个建议 —…

音频导入规范

一般音频可以交给策划来导入提交&#xff0c;需要遵循一些规范&#xff0c;下面是我们实际项目用到的一些规范 1、Force To Mono&#xff1a; 勾选&#xff0c;强制单声道。&#xff08;可以减少音效文件的内存占用&#xff09; 2、Normalize&#xff1a; 勾选&#xff0c;引…

使用html写一个倒计时页面

一个使用 HTML、CSS 和 JavaScript 实现的倒计时页面,包含动态效果和响应式布局: 功能特点: 动态效果: 每个时间单元带有 hover 动画(悬浮时轻微上浮)倒计时数字实时更新,精确到秒结束时自动更换背景颜色并显示提示信息响应式设计: 适配移动端屏幕(屏幕宽度小于600px…

spring boot源码和lib分开打包

1.项目通过maven引入的jar多了之后&#xff0c;用maven打出的jar会非常庞大&#xff0c;我的是因为引入了ffmpeg的相关jar,所以&#xff0c;每次上传服务更新都要传输好久&#xff0c;修改maven打包方式&#xff0c;改为源码和lib分离模式 2.maven的pom.xml配置如下 <build…

计算机网络笔记(三十)——5.2用户数据报协议UDP

5.2.1UDP概述 一、UDP 的定义 用户数据报协议 (User Datagram Protocol, UDP) 是传输层的无连接、不可靠协议。它提供最小化的协议机制&#xff0c;仅支持数据报的简单传输&#xff0c;不保证数据顺序或可靠性。 二、UDP 的核心特点 无连接 通信前无需建立连接&#xff0c;直…

Java异步编程之消息队列疑难问题拆解

前言 在Java里运用消息队列实现异步通信时&#xff0c;会面临诸多疑难问题。这里对实际开发中碰到的疑难为题进行汇总及拆解&#xff0c;使用RabbitMQ和Kafka两种常见的消息队列中间件来作为示例&#xff0c;给出相应的解决方案&#xff1a; 一、消息丢失问题 消息在传输过程…

香橙派3B学习笔记10:snap打包C/C++程序与动态链接库(.so)

esnap打包C/C程序与动态链接库&#xff08;.so&#xff09; 之前已经学会了snap基本的打包程序&#xff0c;现在试试打包C/C程序与动态链接库&#xff08;.so&#xff09; ssh &#xff1a; orangepi本地ip 密码 &#xff1a; orangepi 操作系统发行版&#xff1a; 基于 Ubun…

【Python工具开发】k3q_arxml 简单但是非常好用的arxml编辑器,可以称为arxml杀手包

k3q_arxml 介绍 仓库地址1 仓库地址2 极简的arxml编辑库&#xff0c;纯python实现 用法 from pprint import pp # 可以美化打印对象&#xff0c;不然全打印在一行 import k3q_arxml # 加载arxml文件 io_arxml k3q_arxml.IOArxml(filepaths[test/model_merge.arxml])# 打印…

【CSS-8】深入理解CSS选择器权重:掌握样式优先级的关键

CSS选择器权重是前端开发中一个基础但极其重要的概念&#xff0c;它决定了当多个CSS规则应用于同一个元素时&#xff0c;哪条规则最终会被浏览器采用。理解权重机制可以帮助开发者更高效地编写和维护CSS代码&#xff0c;避免样式冲突带来的困扰。 1. 什么是CSS选择器权重&…

大语言模型原理与书生大模型提示词工程实践-学习笔记

&#x1f4d8; 第五期书生葡语实战营讲座总结 &#x1f399; 主讲人&#xff1a;王明&#xff08;东部大学 数据挖掘实验室 博士生&#xff09; 一、大语言模型的生成原理 架构基础&#xff1a;采用 Transformer&#xff08;Decoder-only&#xff09;架构&#xff0c;如 GPT …

李沐 《动手学深度学习》 | 实战Kaggle比赛:预测房价

文章目录 1.下载和缓存数据集2.数据预处理读取样本预处理样本数值型特征处理特征标准化的好处离散值处理转换为张量表示 训练K折交叉验证模型选择最终模型确认及结果预测代码总结提交到Kaggle 房价预测比赛链接&#xff1a;https://www.kaggle.com/c/house-prices-advanced-reg…

一键部署Prometheus+Grafana+alertmanager对网站状态进行监控

在建设监控体系的过程中&#xff0c;针对一个系统的监控是多维度的&#xff0c;除了服务器资源状态、中间件状态、应用状态外&#xff0c;对系统访问状态的监控也是很有必要&#xff0c;可以在系统访问出现异常时第一时间通知到我们。本文介绍使用 Docker-compose 方式一键部署…

康谋方案 | 高精LiDAR+神经渲染3DGS的完美融合实践

目录 一、从点云到高精地图的重建 1、数据采集 2、点云聚合 3、高精地图建模 4、三维建模与装饰 二、颠覆性革新&#xff1a;NeRF 与 3DGS 重建 1、仅需数日&#xff0c;完成街景重建 2、进一步消除 Domain gap&#xff0c;场景逼真如实地拍摄 3、降本增效&#xff0c…

MySQL-事务(TRANSACTION-ACID)管理

目录 一、什么是事务&#xff1f; 1.1.事务的定义 1.2.事务的基本语句 1.3.事务的四大特性&#xff08;ACID&#xff09; 二、数据库的并发控制 2.1.什么是并发及并发操作带来的影响&#xff1f; 2.2.并发操作带来的隔离级别 三、使用事务的场景 3.1.银行转账场景示例 3.2.模拟…

centos系统docker配置milvus教程

本人使用的是京东云服务器配置milvus 参考教程&#xff1a;https://blog.csdn.net/withme977/article/details/137270087 首先确保安装了docker 、docker compose docker -- version docker-compose --version创建milvus工作目录 mkdir milvus # 进入到新建的目录 cd milvu…

什么是JSON ?从核心语法到编辑器

一、什么是JSON &#xff1f; JSON&#xff0c;即 JavaScript 对象表示法&#xff0c;是一种轻量级、跨语言、纯文本的数据交换格式 。它诞生于 JavaScript 生态&#xff0c;但如今已成为所有编程语言通用的 “数据普通话”—— 无论前端、后端&#xff0c;还是 Python、Java&…

计算机网络(7)——物理层

1.数据通信基础 1.1 物理层基本概念 物理层(Physical Layer)是所有网络通信的物理基础&#xff0c;它定义了在物理介质上传输原始比特流(0和1)所需的机械、电气、功能、过程和规程特性 1.2 数据通信系统模型 信源&#xff1a;生成原始数据的终端设备&#xff0c;常见形态包括…

深度学习基础知识总结

1.BatchNorm2d 加速收敛&#xff1a;Batch Normalization 可以使每层的输入保持较稳定的分布&#xff08;接近标准正态分布&#xff09;&#xff0c;减少梯度更新时的震荡问题&#xff0c;从而加快模型训练速度。 减轻过拟合&#xff1a;批归一化引入了轻微的正则化效果&#…

iOS 抖音首页头部滑动标签的实现

抖音首页的头部滑动标签(通常称为"Segmented Control"或"Tab Bar")是一个常见的UI组件&#xff0c;可以通过以下几种方式实现&#xff1a; 1. 使用UISegmentedControl 最简单的实现方式是使用系统自带的UISegmentedControl&#xff1a; let segmentedCo…

ThreadLocal实现原理

ThreadLocal 是 Java 中实现线程封闭&#xff08;Thread Confinement&#xff09;的核心机制&#xff0c;它通过为每个线程创建变量的独立副本来解决多线程环境下的线程安全问题。 Thread └── ThreadLocalMap (threadLocals) // 每个线程持有的专属Map├── Entry[] tab…