C++ queue、priority_queue原始碼解析
技術標籤:C++
重點總結:
std::queue
是一個先進先出(FIFO)的容器,故std::queue
不提供元素的任何迭代器操作。std::queue
是一種容器介面卡,std::deque和std::list滿足FIFO操作需求。queue底層預設使用std::deque
。std::queue
容器只能訪問頭元素和尾元素。只能在容器的末尾新增新元素,只能從頭部移除元素。
std::priority_queue
是一種容器介面卡,根據嚴格的弱排序準則,它的第一個元素總是它所包含的元素中最大的。std::priority_queue
預設情況下,如果沒有為特定優先順序佇列類例項化指定容器類,底層預設使用std::vector
。
1. std::queue 建構函式
std::queue
底層容器可以是標準容器類模板之一,也可以是其他特定設計的容器類。該底層容器應至少支援以下操作:
empty(),size(),front(),back(),push_back(),pop_front()
,STL中deque
和list
滿足這些要求。預設情況下,如果沒有為特定佇列類例項化指定容器類,則使用標準容器deque
#include<queue>
queue
建構函式有如下幾種型別www.cplusplus.com。
- [1]. 通過容器物件進行構造
- [2]. 同上,引數為右值右值
- [3]. std::allocator物件作為引數
- [4]. 通過容器物件、std::allocator進行構造
- [5]. 同上,引數
ctnr
為右值右值 - [6]. 複製構造 with std::allocator
- [7]. 移動構造 with std::allocator
int main ()
{
std::deque<int> mydeck(3,100); // deque with 3 elements
std::list<int> mylist(2,200); // list with 2 elements
std::queue<int> first; // empty queue
std::queue<int> second(mydeck); // [1]. 複製構造,通過deque
std::queue<int,std::list<int> > third; // empty queue with list as underlying container
std::queue<int,std::list<int> > fourth(mylist);
std::cout << "size of first: " << first.size() << '\n';
std::cout << "size of second: " << second.size() << '\n';
std::cout << "size of third: " << third.size() << '\n';
std::cout << "size of fourth: " << fourth.size() << '\n';
return 0;
}
size of first: 0
size of second: 3
size of third: 0
size of fourth: 2
2. std::queue 常用函式
常用函式如下圖,本小節按圖中函式名稱順序,依次函式進行介紹:
- <1>.
back()
返回尾元素引用。front()
返回頭元素引用,原型如下:
reference& back();
const_reference& back() const;
reference& front();
const_reference& front() const;
int main ()
{
std::queue<int> myqueue;
myqueue.push(12);
myqueue.push(75); // this is now the back
myqueue.back() -= myqueue.front();
std::cout << "myqueue.back() is now " << myqueue.back() << '\n';
return 0;
}
myqueue.back() is now 63
- <2>.
emplace()
在queue尾部新增元素,移動語義。
push()
在queue尾部新增元素。
pop()
刪除queue頭部地元素,函式原型如下:
template <class... Args> void emplace(Args&&... args); // emplace
void push(const value_type& val); // push
void push(value_type&& val);
void pop(); // pop
int main ()
{
std::queue<int> myints;
myints.push(1);
myints.push(2);
myints.emplace(3);
myints.emplace(4);
while (!myints.empty()){
std::cout << myints.front() << " ";
myints.pop();
}
return 0;
}
1 2 3 4
- <3>.
empty()
返回容器是否為空,size()
返回容器元素個數。,原型如下:
bool empty() const;
size_type size() const;
int main ()
{
std::queue<int> myints;
std::cout << "myints. empty: " << myints.empty() << '\n';
std::cout << "myints. size: " << myints.size() << '\n';
for (int i=0; i<5; i++)
myints.push(i);
std::cout << "myints. empty: " << myints.empty() << '\n';
std::cout << "myints. size: " << myints.size() << '\n';
return 0;
}
myints. empty: 1
myints. size: 0
myints. empty: 0
myints. size: 5
- <4>.
swap()
,將本容器中的元素與引數容器中的元素互換,原型如下:
void swap (queue& x) noexcept(/*see below*/);
int main ()
{
std::queue<int> foo,bar;
foo.push (10); foo.push(20); foo.push(30);
bar.push (111); bar.push(222);
foo.swap(bar);
std::cout << "size of foo: " << foo.size() << '\n';
std::cout << "size of bar: " << bar.size() << '\n';
return 0;
}
size of foo: 2
size of bar: 3
3. std::priority_queue
3.1 簡介
std::vector
和std::deque
滿足priority_queue
底層功能要求。預設情況下,如果沒有為特定優先順序佇列類例項化指定容器類,則其底層實現使用std::vector
。
為了始終在其內部保持堆結構(其內部預設是大頂堆),需要支援隨機訪問迭代器。這是由容器介面卡自動完成的,在需要時自動呼叫演算法函式make_heap
、push_heap
和pop_heap
,
priority_queue
與queue
不同點在於我們可以自定義其中資料的優先順序, 讓優先順序高的排在佇列前面,優先出隊。預設情況下大的元素在前。
3.2 常用函式
- <1> .
priority_queue
常用函式如下圖,和queue
基本操作相同。
top() 訪問隊頭元素
empty() 佇列是否為空
size() 返回佇列內元素個數
push() 插入元素到隊尾 (並排序)
emplace() 原地構造一個元素並插入佇列
pop() 彈出隊頭元素
swap() 交換內容
3.3 建構函式
stl_queue.h中有如下定義:
template<typename _Tp,
typename _Sequence = vector<_Tp>,
typename _Compare = less<typename_Sequence::value_type> >
class priority_queue { …
其中,_Tp
為資料型別,_Sequence
是容器型別(必須是用陣列實現的容器,如 vector,deque等等,但不能用 list。STL裡面預設用的是vector),_Compare
是比較函式(default: less<T>
)。
當需要用自定義的資料型別時才需要傳入這三個引數,使用基本資料型別時,只需要傳入資料型別,預設是大頂堆。
優先順序的佇列建構函式有五種型別,主要使用的有以下兩種:
- [1]. 無輸入引數,
stl_queue.h
中原始碼如下所示。
explicit
priority_queue(const _Compare& __x = _Compare(),
_Sequence&& __s = _Sequence()) : c(std::move(__s)), comp(__x) {
std::make_heap(c.begin(), c.end(), comp); }
例:
int main()
{
// 以下兩句相同
std::priority_queue<int> pri_queue;
priority_queue<int, vector<int>, less<int>> pri_queue_temp;
pri_queue.push(1); pri_queue.push(3); pri_queue.push(2);
while (!pri_queue.empty())
{
cout << pri_queue.top() << ' ';
pri_queue.pop();
}
cout << endl; // 列印: 3 2 1
return 0;
}
- [2]. 通過迭代器
[first,last)
構造物件,stl_queue.h
中原始碼如下所示。
template<typename _InputIterator>
priority_queue(_InputIterator __first, _InputIterator __last,
const _Compare& __x = _Compare(),
_Sequence&& __s = _Sequence()) : c(std::move(__s)), comp(__x) {
__glibcxx_requires_valid_range(__first, __last);
c.insert(c.end(), __first, __last);
std::make_heap(c.begin(), c.end(), comp);
}
例:
int main()
{
int myints[]= {10,60,50,20};
// 以下兩句相同
std::priority_queue<int> second(myints,myints+4);
std::priority_queue<int, std::vector<int>, std::greater<int>> third(myints,myints+4);
while (!second.empty())
{
cout << second.top() << ' ';
second.pop();
}
cout << endl; // 列印結果 60 50 20 10
return 0;
}
3.3 自定義型別作為元素,過載<函式
- 自定義型別的物件中需過載
<
函式 - _Compare函式需重寫
()
函式,程式碼如下:
class TestClass {
public:
int x;
TestClass(int a) {x = a;}
//運算子過載<
bool operator<(const TestClass& a) const{
return x < a.x;
}
};
class Test_func {
public:
bool operator() (TestClass a, TestClass b) {
return a.x < b.x;
}
};
int main()
{
// [1]. 使用 priority_queue<TestClass>
priority_queue<TestClass> queue_;
queue_.emplace(TestClass(1));
queue_.emplace(TestClass(3));
queue_.emplace(TestClass(2));
std::cout << "queue_: ";
while (!queue_.empty())
{
cout << queue_.top().x;
queue_.pop();
}
// [2]. 與上述[1]相同
priority_queue<TestClass, vector<TestClass>, Test_func> queue_temp;
queue_temp.emplace(TestClass(1));
queue_temp.emplace(TestClass(3));
queue_temp.emplace(TestClass(2));
std::cout << "\n queue_temp:";
while (!queue_temp.empty())
{
cout << queue_temp.top().x;
queue_temp.pop();
}
return 0;
}
queue_: 321
queue_temp:321