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
繁體地址,請注明出處:http://hk.pswp.cn/news/909084.shtml
英文地址,請注明出處:http://en.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…