Foreword : I have read Mr. Hou's "STL Source Code Analysis" before, but that was many years ago. Now, sometimes you need to understand the implementation of STL used in actual work to check problems and crashes at work. Therefore, it is planned to go through the source code of STL again.
Abstract : This article describes allocator
the implementation of libcxx in llvm.
Keywords : stack
, queue
, priority_queue
Others : Reference code LLVM-libcxx
Note : The implementation of llvm is different from the implementation of gnu and msvc when referring to the code. The article assumes that you are already very familiar with the data structure, and will not delve into the details of the corresponding data structure.
There are three commonly used containers in C++, which are not containers but adapters, that is, they actually use existing containers to realize the functions of corresponding containers.
stack
Provides FILO (First In Last Out) capability.queue
Provides FIFO capability.priority_queue
priority queue.
1 stack
stack
The implementation in STL is passed by default deque
, and of course the container can also be specified, but the premise is that the corresponding container contains some operation interfaces. It stack
can also be seen from the adapter that another container is wrapped in this class, and then the interface of the container is used to realize the required functions. If you expect to use a custom container, the corresponding container should support pop_back(), push_back(), size(), empty()
such interfaces.
template <class _Tp, class _Container /*= deque<_Tp>*/>
class _LIBCPP_TEMPLATE_VIS stack{
public:
typedef _Container container_type;
typedef typename container_type::value_type value_type;
typedef typename container_type::reference reference;
typedef typename container_type::const_reference const_reference;
typedef typename container_type::size_type size_type;
static_assert((is_same<_Tp, value_type>::value), "" );
protected:
container_type c;
};
stack
The implementation of is relatively simple, there is nothing to say. It should be noted that it is implemented top
separately pop
instead of directly implementing an interface to ensure exception safety. If it is implemented in the same interface, it is normal when the element is obtained, the data is popped, and the abnormal data will be lost when copying the data object during the return process.
void push(const value_type& __v) {
c.push_back(__v);}
const_reference top() const {
return c.back();}
void pop() {
c.pop_back();}
2 queue
queue
The stack
same as the definition, the default is to wrap adeque
template <class _Tp, class _Container /*= deque<_Tp>*/>
class _LIBCPP_TEMPLATE_VIS queue{
public:
typedef _Container container_type;
typedef typename container_type::value_type value_type;
typedef typename container_type::reference reference;
typedef typename container_type::const_reference const_reference;
typedef typename container_type::size_type size_type;
static_assert((is_same<_Tp, value_type>::value), "" );
protected:
container_type c;
};
deque
The implementation is also very simple, not much description.
bool empty() const {
return c.empty();}
_LIBCPP_INLINE_VISIBILITY
size_type size() const {
return c.size();}
_LIBCPP_INLINE_VISIBILITY
reference front() {
return c.front();}
_LIBCPP_INLINE_VISIBILITY
const_reference front() const {
return c.front();}
_LIBCPP_INLINE_VISIBILITY
reference back() {
return c.back();}
_LIBCPP_INLINE_VISIBILITY
const_reference back() const {
return c.back();}
_LIBCPP_INLINE_VISIBILITY
void push(const value_type& __v) {
c.push_back(__v);}
3 priority_queue
The priority queue is a heap, and the data in the container is ordered to some extent. The implementation of the priority queue is somewhat different from the previous two. The default is that vector
because the elements in the container are ordered to some extent, a comparison function needs to be provided.
template <class _Tp, class _Container = vector<_Tp>, class _Compare = less<typename _Container::value_type> >
class _LIBCPP_TEMPLATE_VIS priority_queue{
public:
typedef _Container container_type;
typedef _Compare value_compare;
typedef typename container_type::value_type value_type;
typedef typename container_type::reference reference;
typedef typename container_type::const_reference const_reference;
typedef typename container_type::size_type size_type;
static_assert((is_same<_Tp, value_type>::value), "" );
protected:
container_type c;
value_compare comp;
};
std::make_heap
Create a heap on the current container is called when the heap is constructed . The specific implementation is not detailed now, and I will describe it in detail when I look at the algorithm implementation (it is estimated to be a general heap construction algorithm).
template <class _Tp, class _Container, class _Compare>
inline priority_queue<_Tp, _Container, _Compare>::priority_queue(const value_compare& __comp, container_type&& __c)
: c(_VSTD::move(__c)),
comp(__comp){
_VSTD::make_heap(c.begin(), c.end(), comp);
}
Both inserting and popping elements also call std
the implementation of the algorithm.
template <class _Tp, class _Container, class _Compare>
inline void priority_queue<_Tp, _Container, _Compare>::push(value_type&& __v){
c.push_back(_VSTD::move(__v));
_VSTD::push_heap(c.begin(), c.end(), comp);
}
template <class _Tp, class _Container, class _Compare>
inline void priority_queue<_Tp, _Container, _Compare>::pop(){
_VSTD::pop_heap(c.begin(), c.end(), comp);
c.pop_back();
}