1. 程式人生 > 其它 >C++ queue、priority_queue原始碼解析

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中dequelist滿足這些要求。預設情況下,如果沒有為特定佇列類例項化指定容器類,則使用標準容器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::vectorstd::deque滿足priority_queue底層功能要求。預設情況下,如果沒有為特定優先順序佇列類例項化指定容器類,則其底層實現使用std::vector

  為了始終在其內部保持堆結構(其內部預設是大頂堆),需要支援隨機訪問迭代器。這是由容器介面卡自動完成的,在需要時自動呼叫演算法函式make_heappush_heappop_heap

  priority_queuequeue不同點在於我們可以自定義其中資料的優先順序, 讓優先順序高的排在佇列前面,優先出隊。預設情況下大的元素在前。

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