C++ STL源码剖析之容器配接器stack与queue、priority_queue

0.导语

为何stack与queue不被称为容器呢?

下面本节带着这个问题来深入源码分析。

1.stack

在stack的源码中我们关注两点:- 默认_Sequencedeque- 内部函数实现是调用_Sequence对应容器的函数。

stack.png

  1. template<typename _Tp, typename _Sequence = deque<_Tp> >
  2. class stack
  3. {
  4. public:
  5. typedef typename _Sequence::value_type value_type;
  6. typedef typename _Sequence::reference reference;
  7. typedef typename _Sequence::const_reference const_reference;
  8. typedef typename _Sequence::size_type size_type;
  9. typedef _Sequence container_type;
  10. protected:
  11. // See queue::c for notes on this name.
  12. _Sequence c;
  13. public:
  14. reference
  15. top()
  16. {
  17. __glibcxx_requires_nonempty();
  18. return c.back();
  19. }
  20. void
  21. push(const value_type& __x)
  22. { c.push_back(__x); }
  23. }

测试stack底层容器

对于stack来说,底层容器可以是vectordequelist,但不可以是mapset。由于编译器不会做全面性检查,当调用函数不存在的时候,就编译不通过,所以对于像set虽然不能作为底层容器,但如果具有某些函数,调用仍然是成功的,直到调用的函数不存在。

  1. int test_stack() {
  2. cout<<"============test_stack============="<<endl;
  3. clock_t timeStart = clock();
  4. stack<int, list<int>> c;
  5. for (long i = 0; i < 100000; i++)
  6. c.push(i+1);
  7. cout << "stack.size()= " << c.size() << endl;
  8. cout << "stack.top()= " << c.top() << endl;
  9. c.pop();
  10. cout << "stack.size()= " << c.size() << endl;
  11. cout << "stack.top()= " << c.top() << endl;
  12. cout << "use stack milli-seconds : " << (clock() - timeStart) << endl;
  13. timeStart=clock();
  14. stack<int, deque<int>> c1;
  15. for (long i = 0; i < 100000; i++)
  16. c1.push(i+1);
  17. cout << "stack.size()= " << c1.size() << endl;
  18. cout << "stack.top()= " << c1.top() << endl;
  19. c1.pop();
  20. cout << "stack.size()= " << c1.size() << endl;
  21. cout << "stack.top()= " << c1.top() << endl;
  22. cout << "use stack milli-seconds : " << (clock() - timeStart) << endl;
  23. // vector可以作为stack的底层容器
  24. stack<int, vector<int>> c2;
  25. for (long i = 0; i < 100000; i++)
  26. c2.push(i+1);
  27. cout << "stack.size()= " << c2.size() << endl;
  28. cout << "stack.top()= " << c2.top() << endl;
  29. c2.pop();
  30. cout << "stack.size()= " << c2.size() << endl;
  31. cout << "stack.top()= " << c2.top() << endl;
  32. cout << "use stack milli-seconds : " << (clock() - timeStart) << endl;
  33. }

2.queue

在queue的源码中我们关注两点:- 默认_Sequencedeque- 内部函数实现是调用_Sequence对应容器的函数。

  1. template<typename _Tp, typename _Sequence = deque<_Tp> >
  2. class queue
  3. {
  4. public:
  5. typedef typename _Sequence::value_type value_type;
  6. typedef typename _Sequence::reference reference;
  7. typedef typename _Sequence::const_reference const_reference;
  8. typedef typename _Sequence::size_type size_type;
  9. typedef _Sequence container_type;
  10. protected:
  11. _Sequence c;
  12. public:
  13. void push(const value_type& __x)
  14. { c.push_back(__x); }
  15. void pop()
  16. {
  17. __glibcxx_requires_nonempty();
  18. c.pop_front();
  19. }
  20. }

其对应的UML类图如下:

queue_.png

同理,优先队列则是使用vector作为默认容器。

  1. template<typename _Tp, typename _Sequence = vector<_Tp>,
  2. typename _Compare = less<typename _Sequence::value_type> >
  3. class priority_queue
  4. {
  5. public:
  6. typedef typename _Sequence::value_type value_type;
  7. typedef typename _Sequence::reference reference;
  8. typedef typename _Sequence::const_reference const_reference;
  9. typedef typename _Sequence::size_type size_type;
  10. typedef _Sequence container_type;
  11. protected:
  12. // See queue::c for notes on these names.
  13. _Sequence c;
  14. _Compare comp;
  15. public:
  16. reference
  17. top()
  18. {
  19. __glibcxx_requires_nonempty();
  20. return c.front();
  21. }
  22. void
  23. push(const value_type& __x)
  24. {
  25. c.push_back(__x);
  26. std::push_heap(c.begin(), c.end(), comp);
  27. }
  28. }

priority_queue.png

测试这两个容器配接器支持的底层容器:

queue

对于queue底层容器可以是deque,也可以是list,但不能是vector,map,set,使用默认的deque效率在插入方面比其他容器作为底层要快!

  1. int test_queue() {
  2. cout<<"============test_queue============="<<endl;
  3. clock_t timeStart = clock();
  4. queue<int, list<int>> c;
  5. for (long i = 0; i < 100000; i++) {
  6. c.push(i+1);
  7. }
  8. cout << "stack.size()= " << c.size() << endl;
  9. cout << "stack.front()= " << c.front() << endl;
  10. c.pop();
  11. cout << "stack.size()= " << c.size() << endl;
  12. cout << "stack.front()= " << c.front() << endl;
  13. cout << "use list milli-seconds : " << (clock() - timeStart) << endl;
  14. timeStart=clock();
  15. queue<int, deque<int>> c1;
  16. for (long i = 0; i < 100000; i++) {
  17. c1.push(i+1);
  18. }
  19. cout << "stack.size()= " << c1.size() << endl;
  20. cout << "stack.front()= " << c1.front() << endl;
  21. c1.pop();
  22. cout << "stack.size()= " << c1.size() << endl;
  23. cout << "stack.front()= " << c1.front() << endl;
  24. cout << "use deque milli-seconds : " << (clock() - timeStart) << endl;
  25. }

priority_queue

对于优先队列来说,测试结果发现,采用deque要比默认的vector插入速度快!底层支持vector、deque容器,但不支持list、map、set。

  1. int test_priority_queue() {
  2. cout<<"============test_priority_queue============="<<endl;
  3. clock_t timeStart = clock();
  4. priority_queue<int, deque<int>> c1;
  5. for (long i = 0; i < 100000; i++) {
  6. c1.push(i+1);
  7. }
  8. cout << "stack.size()= " << c1.size() << endl;
  9. cout << "stack.top()= " << c1.top() << endl;
  10. c1.pop();
  11. cout << "stack.size()= " << c1.size() << endl;
  12. cout << "stack.top()= " << c1.top() << endl;
  13. cout << "use deque milli-seconds : " << (clock() - timeStart) << endl;
  14. priority_queue<int, vector<int>> c2;
  15. for (long i = 0; i < 100000; i++)
  16. c2.push(i+1);
  17. cout << "stack.size()= " << c2.size() << endl;
  18. cout << "stack.top()= " << c2.top() << endl;
  19. c2.pop();
  20. cout << "stack.size()= " << c2.size() << endl;
  21. cout << "stack.top()= " << c2.top() << endl;
  22. cout << "use stack milli-seconds : " << (clock() - timeStart) << endl;
  23. }

因此,stack、queue、priority_queue不被称为容器, 把它称为容器配接器。