循環緩衝區C++的一種實現

之前去騰訊面試的時候被問到的一道題目:實現一個循環緩衝區(不帶互斥鎖)。仔細一想,其實和循環隊列的思想一模一樣,還是怪自己數據結構沒學好阿(其實我是學通信的,所以最近在惡補)。還是先上代碼

頭文件如下,CircleBuffer.h

#ifndef _CIRCLE_BUFFER_H_
#define _CIRCLE_BUFFER_H_

//basic data type rename
typedef unsigned char uchar8;
typedef char char8;
typedef unsigned int uint32;
typedef int int32;

//definition of CircleBuffer
class CircleBuffer
{
public:
	CircleBuffer(int sz);//constructor
	~CircleBuffer();//destructor
	void ClearBuffer();//clear the buffer to init state, just modify the indicator
	bool IsBufferFull();//to judge if the buffer is full
	bool IsBufferEmpty();//to judge if the buffer is empty
	uint32 BufferLength();//calculate buffer's length
	uint32 ReadBuffer(uchar8 *data, uint32 len);//read data from the buffer, return the actual data length
	uint32 WriteBuffer(uchar8 *data, uint32 len);//write data into the buffer, return the actual written-in data length
	void ShowBuffer();//print single element one by one starting from pread, not from buf[0] for sure
private:
	uchar8 *buf;//used to store the data;
	uint32 size;//indicates the size of the buffer;
	uint32 pwrite;//indicates the start position of a write operation;
	uint32 pread;//indicates the start position of a read operation;
	inline uint32 NextPos(uint32 cur);//get the next pos of write or read
};

#endif //_CIRCLE_BUFFER_H_
源文件如下,CircleBuffer.cpp

#include <iostream>
#include "CircleBuffer.h"

CircleBuffer::CircleBuffer(int sz)
{
	size = sz+1;
	buf = new uchar8[sz+1];
	if(buf == 0)
	{
		std::cout << __FUNCTION__ << __LINE__  << ":new failed..." << std::endl;
	}
	pwrite = 0;
	pread = 0;
}

CircleBuffer::~CircleBuffer()
{
	if(buf)
	{
		delete[] buf;
		buf = 0;
	}
}

void CircleBuffer::ClearBuffer()
{
	pwrite = 0;
	pread = 0;
}

inline uint32 CircleBuffer::NextPos(uint32 cur)
{
	return (cur + 1) % size;
}

bool CircleBuffer::IsBufferFull()
{
	if(NextPos(pwrite) == pread)
		return true;
	return false;
}

bool CircleBuffer::IsBufferEmpty()
{
	if(pwrite == pread)
		return true;
	return false;
}

uint32 CircleBuffer::BufferLength()
{
	return (pwrite - pread + size) % size;
}

uint32 CircleBuffer::ReadBuffer(uchar8 *data, uint32 len)
{
	uint32 cur = pread;
	uint32 i = 0;
	while(cur != pwrite && i < len)
	{
		data[i++] = buf[cur];
		cur = NextPos(cur);
	}
	pread = cur;
	return i;
}

uint32 CircleBuffer::WriteBuffer(uchar8 *data, uint32 len)
{
	uint32 i = 0;
	uint32 cur = pwrite;
	while(NextPos(cur) != pread && i < len)
	{
		buf[cur] = data[i++];
		cur = NextPos(cur);
	}
	if(i < len)
	{
		std::cout << "buffer full ~!!!, lost " << len-i << " bytes" << std::endl;
	}
	pwrite = cur;
	return i;
}

void CircleBuffer::ShowBuffer()
{
	uint32 start = pread;
	while(start != pwrite)
	{
		std::cout << buf[start];
		start = NextPos(start);
	}
	printf("^\n");
}

        其核心思想就是循環隊列。對於一個大小爲size的緩衝區,在分配內存時,多分配一個字節。這樣,Buffer寫滿時,寫指針就在讀指針的前一個位置(邏輯上),即判滿條件;而判空條件則是寫指針和讀指針在同一個位置。最後多餘的一個字節不存儲數據,僅僅用來區分buffer是滿還是空。由於是一個循環緩衝區,所以對遍歷指針cur不能用++這樣的操作,我在上面封裝了一個求當前指針位置的下一個位置的函數uint32 NextPos(uint32 cur); 聲明爲內聯函數,以提高效率。

        由於測試環境不夠複雜,以上代碼只在我的ubuntu os電腦上作了一個簡單的測試:兩個線程,一個讀,一個寫,寫得更快的時候會出現buffer滿的提示。打印buffer的長度爲構造函數指定的長度,pread和pwrite的位置也正確,調用ShowBuffer挨個打印出的字符順序也都正確(從pread開始)。

        小弟經驗不足,僅供參考。並希望各位朋友指出不足之處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章