这里的STL源码结构解析的版本均是G++ 4.4.4。该源代码可以在gun中下载,如果有安装linux,一个STL源码存储再/usr/include/c++/目录下。
文章先对STL中的stack,queue,priority_queue的异同点进行解析,接着分别解析了stack,queue,priority_queue的源码结构实现方式。
stack,queue,priority_queue的相同点
之所以把stack,queue,priority_queue三者放在一起,是因为这三种容器再STL中均属于特殊容器。他们都具有如下特征:
- 它们均是为满足特定需求而建立的容器。stack是为了满足FILO,queue是为了满足FIFO,priority_queue则是为了满足优先级高的元素先出队列。
- 它们均具有一组含义非常明确的函数接口,比如stack的top,pop, push分别表示获取栈顶元素,出栈和入栈,queue的front,back, pop, push分别表示获取队列首元素,队列尾元素,出队列和入队列。
- 它们均不是标准的STL容器,却都是以标准STL容器为基础。stack和queue默认是在deque的基础上封装的,priority_queue默认是在vector的基础上封装出来的。stack和queue的基础容器之所以选择deque而不选择vector等容器,是因为deque在删除元素的时候可以释放空间,同时在重新申请空间的时候无需拷贝所有元素。
- 它们均对基础容器有一定的要求,这个要求是由他们满足的需求确定的。比如stack需要从栈顶进出元素和获取栈顶元素,它的基础容器则需要提供back(), push_back(), pop_back()的函数。同理queue的基础容器需要能够提供back(), push_back(), pop_front(), priority_queue的基础容器需要提供back(), push_back(), pop_back()的函数。
- 你可以你的需要,通过参数传递修改它们的基础容器。比如
stack < int , vector < int > > st
对于stack和queue还有一个共同点,他们均有重载比较运算符,也就是说你可以对二个stack或者二个queue进行字典许的比较和排序。
STL的stack源码解析
stack的模板可以传递二个参数,第一个是元素类型,第二个是可选的容器类型。类里面有成员变量容器_Sequence c。构造函数有二个,一个函数的参数是左引用的,另外一个函数的参数为右引用的。
class stack
{
protected :
_Sequence c ;
private :
explicit
stack ( const _Sequence & __c )
: c ( __c ) { }
explicit
stack ( _Sequence && __c = _Sequence ())
: c ( std :: move ( __c )) { }
}
stack的特定需求的函数则调用的是c的对应函数,比如下面的代码push函数调用的是c的push_back。
{ c . push_back ( __x ) ; }
void pop ()
{ c . pop_back () ; }
二个stack的字典序大小也只直接调用容器c的大小比较来实现的。
inline bool operator < ( const stack < _Tp , _Seq >& __x , const stack < _Tp , _Seq >& __y )
{ return __x . c < __y . c ; }
STL的queue源码解析
由于queue的源码原理和实现方式均跟stack相同,这里就省略了。有兴趣的朋友可以自己翻阅源码。
STL的priority_queue源码解析
priority_queue与前面二者不同的是priority_queue不仅仅进行简单的出入队列操作,在这些操作过程,还包括了堆的排序来进行内容的排序。它要求存储元素已经定义了严格的排序关系less,且不提供相等或者大小的比较操作符号。priority_queue属于基于优先级排序的queue,需要注意的是priority_queue默认使用的是最大堆,front()是堆中的最大元素。而且priority_queue在插入和删除元素的时候维护队列的正确性,如果你通过其他方式来操作队列元素或者改变它们的顺序,priority_queue不会重新对队列中的元素重新排序。
priority_queue的模板支持三个参数,第一个参数是存储元素类型,第二个参数是基础容器,默认为vector,第三个参数是比较运算符_Compare,默认为less。为了维护堆的正确性,再构造函数中即对容器进行make_heap的建堆操作。同时你还可以通过传递迭代器的方式来构造 一个priority_queue。
typename _Compare = less < typename _Sequence :: value_type > >
class priority_queue
{
protected :
_Sequence c ;
public :
explicit
priority_queue ( const _Compare & __x ,
const _Sequence & __s )
: c ( __s ) , comp ( __x )
{ std :: make_heap ( c . begin () , c . end () , comp ) ; }
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 ) ;
}
}
一些priority_queue的特殊需求函数跟stack一样也是之间调用容器c的对应函数,比如size()函数和top()函数。
size_type
size () const
{ return c . size () ; }
/**
* Returns a read-only (constant) reference to the data at the first
* element of the %queue.
*/
const_reference
top () const
{
__glibcxx_requires_nonempty () ;
return c . front () ;
}
priority_queue与queue和stack另外二个不同的地方在于push()和pop()函数。因为它需要再push()和pop()操作的时候进行堆的队列维护,即调用push_heap和pop_heap函数。
push ( const value_type & __x )
{
c . push_back ( __x ) ;
std :: push_heap ( c . begin () , c . end () , comp ) ;
}
void
pop ()
{
__glibcxx_requires_nonempty () ;
std :: pop_heap ( c . begin () , c . end () , comp ) ;
c . pop_back () ;
}
到此完成STL中特殊容器stack, queue, priority_queue的源码机制解释。如需深入了解,请查阅源代码和相应书籍。
参考书籍
G++源码,版本4.4.4
Nicolai M.Josuttis, The C++ Standard library