模板化循環緩衝區容器circular_buffer<T, Alloc> |
|
|
||
|
術語 循環緩衝區circular buffer 通常指的是內存中的一塊用於保存輸入數據的區域。當緩衝區被填入時,新的數據從緩衝區的開始被寫入,並覆蓋掉舊的數據。(見圖)
circular_buffer 是一個符合STL的容器。它是一種類似於 std::list
或 std::deque
的序列。它支持隨機訪問迭代器,在緩衝區頭部或尾部的常量時間插入和刪除操作,與 std
算法的互操作能力。circular_buffer
被特別設計為提供固定容量的存儲大小。當其容量被用完時,新插入的元素會覆蓋緩衝區頭部或尾部(取決於使用何種插入操作)的元素。
circular_buffer
僅在創建時、顯式調整容量時,或需要重新調整大小或賦值操作時才進行內存分配。另一方面,還有一個 circular_buffer_space_optimized
可用。它是 circular_buffer
的一個適配器,它不在創建時分配內存,而是在需要時分配內存。
以是一個使用 circular_buffer 的簡單例子:
#include <boost/circular_buffer.hpp>
int main(int /*argc*/, char* /*argv*/[]) {
// 創建一個容量為3個整數的循環緩衝區。
boost::circular_buffer<int> cb(3);
// 插入一些元素到緩衝區。
cb.push_back(1);
cb.push_back(2);
cb.push_back(3);
int a = cb[0]; // a == 1
int b = cb[1]; // b == 2
int c = cb[2]; // c == 3
// 現在緩衝已滿,再插入元素將覆寫最前面的元素。
cb.push_back(4); // 用 4 覆蓋 1.
cb.push_back(5); // 用 5 覆蓋 2.
// 現在緩衝區包含 3, 4 和 5.
a = cb[0]; // a == 3
b = cb[1]; // b == 4
c = cb[2]; // c == 5
// 可以從前端或後端彈出元素。
cb.pop_back(); // 5 被刪除。
cb.pop_front(); // 3 被刪除。
int d = cb[0]; // d == 4
return 0;
}
namespace boost {
|
circular_buffer
背後的動機是創建一個可以與STL無縫使用的容器。另外,circular_buffer
的設計受到以下原則的指導:
circular_buffer_space_optimized
就是一個適配器的例子) 為了達到最高的效率,circular_buffer 將它的元素保存在一片連
續內存中,這樣可以:
circular_buffer 的可能應用包括:
circular_buffer 的:
circular_buffer
的線程安全性與大多數STL實現中的容器的線程安全性相同。即 circular_buffer
不是線程安全的。線程安全僅
在以下情形時被保證:對 circular_buffer
的不同實例進行並發訪問是安全的,對共享的 circular_buffer
進行並發讀訪問是安全的。
如果多個線程訪問同一個 circular_buffer,
且至少有一個線程可能會進行寫訪問,則用戶要負責確保各個線程在訪問容器時互斥。線程間的互斥可以通過對底層的 circular_buffer
操作增加一個鎖獲取和釋放來實現。(請見 有界緩衝區示例)
覆寫操作發生在當一個元素被插入到一個滿的 circular_buffer
中時 -
舊的元素將被新元素所覆蓋。在正式審查時,曾經對"覆寫某個元素"的精確意義進行過討論。它可能是析構一個原有的元素並接著原地構造一個新元素,也可能是
將新元素賦值到舊元素中。circular_buffer 最終實現了賦值操
作,因為它更為高效。
從被保存元素的業務邏輯觀點來看,析構/構造操作與賦值操作其實是一樣的。但是,在極少見的情形下(如果有)它們之間可能有不同。如果 元素本身需要 以析構/構造操作來替換賦值操作,可能考慮對該元素實現一個賦值操作的包裝器,並改為保存該包裝器。需要注意,保存這個包裝器有一個缺點。對該包裝器的每 次賦值都要調用析構/構造操作 - 不僅是在該包裝器被覆寫時(即緩衝區滿時),也會發生在對被保存包裝器進行移位時(例如,在容器中央插入)。
當數據源產生的數據多於固定大小緩衝區可以容納的數量時,通常有以下幾種選擇:
顯然,circular_buffer 實現了第三種選擇。但是它不實現其它選擇 - 尤其是前兩個 -
則可能不太明顯。你可能覺得 circular_buffer
應該實現前面三種選項並提供一個機制來在它們之間進行選擇。這種印象是錯的。circular_buffer
被設計並優化為循環的(這意味著在緩衝區滿時將覆寫最舊的數據)。如果有這樣一種控制機制,它只會把事情弄複雜,而且 circular_buffer
的使用也可能會不夠方便。
此外,前兩個選項(還有第四個)並不要求緩衝區是循環的。如果需要前兩個選項,請考慮實現 std::vector
的一個適配器。這種情況下,circular_buffer 不適合用於適配,因為和 std::vector
相比,它要為其循環行為負出一定的開銷。
當從一個空緩衝區讀取或刪除元素時,緩衝區應可以通知數據消耗者(例如通過拋出一個下溢異常)它沒有元素保存。因為以下兩個原因,circular_buffer
沒有實現這一行為:
std 容器也沒有這樣做。 從一個空的 std 容器中讀取或刪除一個元素(如通過調用 front()
或 pop_back())被認為是 bug,circular_buffer
也一樣。數據消耗者在從容器中讀取/刪除元素之前,必須測試它是否為空。不過,從 circular_buffer
讀取時,有一個選項決定 at() 方法在索引超界時是否拋出異常。
如果一個迭代器所指的元素被刪除或被另一個元素覆寫,通常認為該迭代器將失效。該規則是 對
調試的支持 所強制的,並且被記錄在每一個方法中。不過,有些使用 circular_buffer
的應用可能要求較松的規則:迭代器僅當其指向未初始化內存時才無效。考慮以下例子:
#define BOOST_CB_DISABLE_DEBUG // 調試支持已關閉,否則代碼將引發運行期錯誤。
#include <boost/circular_buffer.hpp>
#include <assert.h>
int main(int /*argc*/, char* /*argv*/[]) {
boost::circular_buffer<int> cb(3);
cb.push_back(1);
cb.push_back(2);
cb.push_back(3);
boost::circular_buffer<int>::iterator it = cb.begin();
assert(*it == 1);
cb.push_back(4);
assert(*it == 4); // 該迭代器仍指向已初始化的內存。
return 0;
}
該迭代器不再指向原來的元素(從"嚴格"的觀點來看,它被認為是無效的),但它仍然指向內存中同一個有效的位置。 circular_buffer
所支持的對迭代器失效的"寬鬆"定義被認為是實現細節,而不是完整的特性。迭代器依然有效的規則可以由 soft_iterator_invalidation.cpp
中的代碼推斷得到。
circular_buffer 不能用於保存指向動態分配對象的指針。當一個
circular_buffer 變滿時,後來的插入將覆寫已保存的指針 - 這會導致內存洩漏。建議採用智能指針[1]來代替。(任何 std::auto_ptr
容器都被認為是非常危險的[2])
雖然 circular_buffer 的內部是循環的,但它的迭代器不是。circular_buffer
的迭代器僅在範圍 [begin(), end()] 內有效。如,迭代器 (begin()
- 1) 和 (end() + 1) 均為無效。
為了幫助程序員避免和發現常見的 bugs, circular_buffer
包括了一些對調試的支持。
circular_buffer
維護了一個有效迭代器列表。一旦任何元素被銷毀,指向該元素的所有迭代器將被從這個列表中刪除,明確失效(設置一個失效標誌)。對調試的支持還包括多個斷
言(BOOST_ASSERT
宏),這樣可以確保 circular_buffer
和它的迭代器在運行期正確使用。當一個無效迭代器被使用時,斷言將報告一個錯誤。顯式的迭代器失效和斷言的使用是一種非常強的調試技術,可以捕捉到大多數
錯誤。
此外,在調試模式下,由 circular_buffer
分配的未初始化內存會用值 0xcc
進行填充。這樣可以幫助程序員在調試代碼時分辨出已初始化和未初始化的內存。詳情請參見源代碼。
對調試的支持僅在調試模式下打開(即在 NDEBUG
未定義時)。也可以通過定義 BOOST_CB_DISABLE_DEBUG 宏來顯式關閉它。
以下例子包含了 circular_buffer 的各種用法。
#include <boost/circular_buffer.hpp>
#include <numeric>
#include <assert.h>
int main(int /*argc*/, char* /*argv*/[])
{
// 創建一個容量為3的循環緩衝區
boost::circular_buffer<int> cb(3);
// 插入一些元素到循環緩衝區
cb.push_back(1);
cb.push_back(2);
// 斷言
assert(cb[0] == 1);
assert(cb[1] == 2);
assert(!cb.full());
assert(cb.size() == 2);
assert(cb.capacity() == 3);
// 再插入其它元素
cb.push_back(3);
cb.push_back(4);
// 求和
int sum = std::accumulate(cb.begin(), cb.end(), 0);
// 斷言
assert(cb[0] == 2);
assert(cb[1] == 3);
assert(cb[2] == 4);
assert(*cb.begin() == 2);
assert(cb.front() == 2);
assert(cb.back() == 4);
assert(sum == 9);
assert(cb.full());
assert(cb.size() == 3);
assert(cb.capacity() == 3);
return 0;
}
該 circular_buffer 的容量為3個 int.
因此,緩衝區的大小不會超過3。算法 accumulate
計算被存元素的總和。circular_buffer 的語義可以從斷言進行推斷。
有界緩衝區通常用於生產者-消費者模式,其中生產者線程產生一些事項並將它們保存在容器中,而消費者線程從容器中取出這些事項並處理它 們。有界緩衝區必須保證當容器滿時,生產者不會插入,同樣當容器空時消費者不會取出,並且每個事項正好被一個消費者消耗。
下例示範了 circular_buffer
如何被用作有界緩衝區的底層容器。
#include <boost/circular_buffer.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>
#include <boost/thread/thread.hpp>
#include <boost/progress.hpp>
#include <boost/bind.hpp>
template <class T>
class bounded_buffer {
public:
typedef boost::circular_buffer<T> container_type;
typedef typename container_type::size_type size_type;
typedef typename container_type::value_type value_type;
explicit bounded_buffer(size_type capacity) : m_unread(0), m_container(capacity) {}
void push_front(const value_type& item) {
boost::mutex::scoped_lock lock(m_mutex);
m_not_full.wait(lock, boost::bind(&bounded_buffer<value_type>::is_not_full, this));
m_container.push_front(item);
++m_unread;
lock.unlock();
m_not_empty.notify_one();
}
void pop_back(value_type* pItem) {
boost::mutex::scoped_lock lock(m_mutex);
m_not_empty.wait(lock, boost::bind(&bounded_buffer<value_type>::is_not_empty, this));
*pItem = m_container[--m_unread];
lock.unlock();
m_not_full.notify_one();
}
private:
bounded_buffer(const bounded_buffer&); // 禁止複製構造函數
bounded_buffer& operator = (const bounded_buffer&); // 禁止賦值操作符
bool is_not_empty() const { return m_unread > 0; }
bool is_not_full() const { return m_unread < m_container.capacity(); }
size_type m_unread;
container_type m_container;
boost::mutex m_mutex;
boost::condition m_not_empty;
boost::condition m_not_full;
};
bounded_buffer 使用了 Boost Threads 和 Boost Bind 庫。
方法 push_front()
被生產者線程調用以插入新事項到緩衝區中。該方法鎖住互斥體並等待至有空間容納新事項。(互斥體在等待期間會被解鎖,當條件滿足時再重新鎖住)。如果緩衝
區中有空間可用,執行將繼續,該方法插入新事項到 circular_buffer
的尾部。然後遞增未讀項計數並解鎖互斥體(如果在互斥體解鎖前拋出異常,互斥體會由 scoped_lock
的析構函數自動解鎖)。最後該方法通知正在等待新事項被插入到緩衝區中的消費者線程。
方法 pop_back()
被消費者線程調用以從緩衝區讀取一個事項。該方法鎖住互斥體並等待至緩衝區中有未讀項。如果有至少一個未讀項,該方法就遞減未讀項計數並從 circular_buffer
讀出下一個事項。然後解鎖互斥體並通知正在等待緩衝區有空間容納新事項的生產者線程。
方法 pop_back() 並不刪除事項,被讀事項保留在 circular_buffer
中,當 circular_buffer
滿時它將被新事項(由生產者插入)所替換。該方法比顯式調用 circular_buffer
的 pop_back()
方法刪除該事項更為高效。這種方法基於以下假設,即新事項的賦值(替換)操作比析構(刪除)舊項並原地構造(插入)新項更為高效。
要和基於不同容器的有界緩衝區進行比較,可以編譯並運行 bounded_buffer_comparison.cpp.
該測試將表明,基於 circular_buffer 的有界緩衝區比基於 std::deque
的有界緩衝區更為高效。(在實踐中的結果可能不一樣,因為該測試會受到如即時CPU負荷等外部因素的影響)。
circular_buffer 定義於文件 boost/circular_buffer.hpp
中。頭文件 boost/circular_buffer_fwd.hpp
中有 circular_buffer 的前向聲明。
隨 機訪問容器Random Access Container, 前 端插入序列Front Insertion Sequence 和 後 端插入序列Back Insertion Sequence。
| 參數 | 說明 | 缺省值 |
|---|
| 類型 | 說明 |
|---|---|
value_type
|
保存在 circular_buffer 中的元素的類型。 |
pointer
|
元素的指針類型。 |
const_pointer
|
元素的常量指針類型。 |
reference
|
元素的引用類型。 |
const_reference
|
元素的常量引用類型。 |
difference_type
|
距離類型。(用於表示兩個迭代器間距離的有符號整數類型) |
size_type
|
大小類型。(用於表示容器距離類型的任一非負值的無符號整數類型) |
allocator_type
|
在 circular_buffer 中使用的分配器類型。 |
const_iterator
|
用於遍歷 circular_buffer
的常量(隨機訪問)迭代器 |
iterator
|
用於遍歷 circular_buffer
的(隨機訪問)迭代器 |
const_reverse_iterator
|
用於反向遍歷 circular_buffer
的常量迭代器。 |
reverse_iterator
|
用於反向遍歷 circular_buffer 的迭代器。 |
array_range
|
數組範圍。(一個 std::pair
的 typedef,其 first 元素是指向數組頭部的指針,second 元素表示數組的大小) |
const_array_range
|
常量數組的範圍。(一個 std::pair
的 typedef,其 first 元素是指向常量數組頭部的指針,second 元素表示常量數組的大小) |
capacity_type
|
容量類型。(和 size_type 一樣 - 為了和 circular_buffer_space_optimized
的一致性而定義) |
explicit
circular_buffer(const allocator_type&
alloc = allocator_type());以最大容量創建一個空的 circular_buffer.
|
explicit
circular_buffer(capacity_type
capacity, const allocator_type&
alloc = allocator_type());以指定容量創建一個空的 circular_buffer.
|
circular_buffer(size_type
n, const_reference
item, const allocator_type&
alloc = allocator_type());以指定容量創建一個滿的 circular_buffer,並填入 item
的 n 份拷貝。
|
circular_buffer(capacity_type
capacity, size_type
n, const_reference
item, const allocator_type&
alloc = allocator_type());以指定容量創建一個 circular_buffer,並填入 item
的 n 份拷貝。
|
circular_buffer(const
circular_buffer<T,Alloc>& cb);複製構造函數。 創建指定
|
template <class InputIterator>創建一個滿的 circular_buffer,以指定區間的拷貝填充。
|
template <class InputIterator>創建一個指定容量的 circular_buffer,以指定區間的拷貝填充。
|
~circular_buffer();析構函數。 銷毀
|
allocator_type
get_allocator() const;取出分配器。
|
allocator_type&
get_allocator();取出分配器的引用。
|
iterator
begin();取得指向 circular_buffer 頭部的迭代器。
|
iterator
end();取得指向 circular_buffer 尾部的迭代器。
|
const_iterator
begin() const;取得指向 circular_buffer 頭部的常量迭代器。
|
const_iterator
end() const;取得指向 circular_buffer 尾部的常量迭代器。
|
reverse_iterator
rbegin();取得指向"反向" circular_buffer 頭部的迭代器。
|
reverse_iterator
rend();取得指向"反向" circular_buffer 的尾部的迭代器。
|
const_reverse_iterator
rbegin() const;取得指向"反向" circular_buffer 頭部的常量迭代器。
|
const_reverse_iterator
rend() const;取得指向"reversed" circular_buffer 尾部的常量迭代器。
|
reference
operator[](size_type
index);取得在 index 位置上的元素。
|
const_reference
operator[](size_type
index) const;取得位於 index 位置的元素。
|
reference
at(size_type
index);取得位於 index 位置的元素。
|
const_reference
at(size_type
index) const;取得位於 index 位置的元素。
|
reference
front();取得第一個元素。
|
reference
back();取得最後一個元素。
|
const_reference
front() const;取得第一個元素。
|
const_reference
back() const;取得最後一個元素。
|
array_range
array_one();取得內部緩衝區的首個連續數組。 該方法與
|
array_range
array_two();取得內部緩衝區的第二個連續數組。 該方法與
|
const_array_range
array_one() const;取得內部緩衝區的首個連續數組。 該方法與
|
const_array_range
array_two() const;取得內部緩衝區的第二個連續數組。 該方法與
|
pointer
linearize();將內部緩衝區線性化至一個連續數組。 該方法用於將所存數據以數組方式傳遞給傳統的 C API。
|
bool is_linearized()
const;這個 circular_buffer 是線性化的嗎?
|
void rotate(const_iterator
new_begin);旋轉 circular_buffer 中的元素。
一個比
|
size_type
size() const;取得當前保存在 circular_buffer 中的元素數量。
|
size_type
max_size() const;取得 circular_buffer 的最大可能大小或容量(取決於分配器的
max_size())。
|
bool
empty() const;circular_buffer 是否為空?
|
bool
full() const;circular_buffer 是否已滿?
|
size_type
reserve() const;取得可以插入到 circular_buffer 中而不覆寫任何已存元素的最大元素數量。
|
capacity_type
capacity() const;取得 circular_buffer 的容量。
|
void
set_capacity(capacity_type
new_capacity);修改 circular_buffer 的容量。
|
void
resize(size_type
new_size, const_reference
item = value_type());修改 circular_buffer 的大小。
|
void
rset_capacity(capacity_type
new_capacity);修改 circular_buffer 的容量。
|
void
rresize(size_type
new_size, const_reference
item = value_type());修改 circular_buffer 的大小。
|
circular_buffer<T,Alloc>&
operator=(const circular_buffer<T,Alloc>& cb);賦值操作符。 使得
|
void
assign(size_type
n, const_reference
item);賦值 n 個 items 到 circular_buffer
中。
|
void
assign(capacity_type
capacity, size_type
n, const_reference
item);賦值 n 個 items 到指定容量的 circular_buffer.
|
template <class InputIterator>將給定區間賦值到 circular_buffer.
|
template <class InputIterator>將給定區間賦值到指定容量的 circular_buffer 中。
|
void
swap(circular_buffer<T,Alloc>& cb);交換兩個 circular_buffers 的內容。
|
void
push_back(const_reference
item = value_type());插入一個新元素到 circular_buffer 的末端。
|
void
push_front(const_reference
item = value_type());插入一個新元素到 circular_buffer 的頭部。
|
void
pop_back();從 circular_buffer 中刪除最後一個元素。
|
void
pop_front();從 circular_buffer 中刪除第一個元素。
|
iterator
insert(iterator
pos, const_reference
item = value_type());在指定位置插入一個元素。
|
void
insert(iterator
pos, size_type
n, const_reference
item);插入 item 的 n
份拷貝到指定位置。
|
template <class InputIterator>將區間 [first, last) 插入到指定位置。
|
iterator
rinsert(iterator
pos, const_reference
item = value_type());在指定位置之前插入一個元素。
|
void
rinsert(iterator
pos, size_type
n, const_reference
item);插入 item 的 n
份拷貝到指定位置之前。
|
template <class InputIterator>將區間 [first, last) 插入到指定位置之前。
|
iterator
erase(iterator
pos);刪除指定位置的一個元素。
|
iterator
erase(iterator
first, iterator
last);刪除區間 [first, last).
|
iterator
rerase(iterator
pos);刪除指定位置的一個元素。
|
iterator
rerase(iterator
first, iterator
last);刪除區間 [first, last).
|
void
clear();刪除 circular_buffer 中的所有元素。
|
template <class T, class Alloc>逐個元素比較兩個 circular_buffers 判斷它們是否相等。
|
template <class T, class Alloc>逐個元素比較兩個 circular_buffers 判斷左操作數是否小於右操作數。
|
template <class T, class Alloc>逐個元素比較兩個 circular_buffers 判斷它們是否不等。
|
template <class T, class Alloc>逐個元素比較兩個 circular_buffers 判斷左操作數是否大於右操作數。
|
template <class T, class Alloc>逐個元素比較兩個 circular_buffers 判斷左操作數小於等於右操作數。
|
template <class T, class Alloc>逐個元素比較兩個 circular_buffers 判斷左操作數是否大於等於右操作數。
|
template <class T, class Alloc>交換兩個 circular_buffers 的內容。
|
Boost 中包含有智能指針的一個很好的實現。
永遠不要創建 std::auto_ptr
的循環緩衝區。詳細討論請參考 Scott
Meyers 的名著 作用ive STL. (Meyers S., Effective
STL: 50 Specific Ways to Improve Your Use of the Standard Template
Library. Addison-Wesley, 2001.)
boost::circular_buffer_space_optimized,
std::vector,
std::list,
std::deque
The circular_buffer has a short
history. Its first version was a std::deque
adaptor. This container was not very effective because of many
reallocations when inserting/removing an element. Thomas Wenish did a
review of this version and motivated me to create a circular buffer
which allocates memory at once when created.
The second version adapted std::vector
but it has been abandoned soon because of limited control over iterator
invalidation.
The current version is a full-fledged STL compliant container. Pavel Vozenilek did a thorough review of this version and came with many good ideas and improvements. Also, I would like to thank Howard Hinnant, Nigel Stewart and everyone who participated at the formal review for valuable comments and ideas.
is_linearized() 和 rotate(const_iterator).
circular_buffer.hpp #includes 完全。circular_buffer(const
allocator_type&)
構造函數的行為。因為這個版本的構造函數不分配任何內存,其容量和大小均設為零。 std::bad_alloc.|
Copyright c 2003-2008 Jan Gaspar Use, modification, and distribution is
subject to the Boost Software License, Version 1.0. |
|