# c++ stl learn
**Repository Path**: zoulizhi/c--stl-learn
## Basic Information
- **Project Name**: c++ stl learn
- **Description**: stl源码学习。。。。。。。。。。。。。。。。。
- **Primary Language**: C++
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2023-03-30
- **Last Updated**: 2023-04-14
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# c++ stl learn
ps:该分析基于minw64 6.4.0 c++
# 1、容器分类
常用容器包括:array,vector,deque,list,set类型,map类型,queue,stack。每种容器所具有的算法如下所示:
## 1.1顺序容器
顺序容器支持下标寻址访问元素。
### 1.1.1 std::array
std::array除了有传统数组支持随机访问、效率高、存储大小固定等特点外,还支持迭代器访问、获取容量、获得原始指针等高级功能。
### 1.1.2 std::vector
是封装动态大小数组的序列容器。支持自动增长,而且,它的增长是一次增长一定的大小,而不是每次都增长1 相对效率较高。支持随机访问,可以像数组一样寻址。
### 1.1.3 std::queue
(双端队列)是一个索引序列容器,允许在其开始和结束时快速插入和删除。此外,deque两端的插入和删除不会使指向其余元素的指针或引用失效。
### 1.1.4 std::list
list是一个容器,它支持从容器中的任意位置对元素进行固定时间的插入和移除。不支持快速随机访问。它通常实现为双链表
## 1.2 有序关联容器
### 1.2.1 std::set
set是一个关联容器,它包含一组Key类型的唯一对象。使用键比较函数Compare进行排序。搜索、删除和插入操作具有对数复杂性。 不支持随机寻址。
### 1.2.2 std::multiset
std::multiset是一个关联容器,其中包含一组Key类型的已排序对象。与set不同,允许多个具有等效值的键。使用键比较函数Compare进行排序。搜索、插入和删除操作具有对数复杂性。
### 1.2.3 std::map
map是一个排序的关联容器,它包含具有唯一键的键值对。使用比较函数Compare对键进行排序。搜索、删除和插入操作具有对数复杂性。实现为红黑树。
### 1.2.4 std::multimap
Multimap是一个关联容器,它包含一个键值对的排序列表,同时允许使用同一个键的多个条目。排序是根据应用于键的比较函数Compare来完成的。搜索、插入和删除操作具有对数复杂性。
不支持随机寻址。
## 1.3无序关联容器
### 1.3.1 unordered_map
是一个关联容器,其中包含具有唯一键的键值对。元素的搜索、插入和删除具有平均不变的时间复杂度。在内部,元素不是按任何特定顺序排序的,而是按bucket组织的。元素放入哪个bucket完全取决于其密钥的散列。这允许对单个元素进行快速访问,因为一旦计算了哈希,它就引用了元素放入的确切bucket。
### 1.3.2 unordered_multimap
### 1.3.3 unordered_set
是一个关联容器,其中包含一组Key类型的唯一对象。搜索、插入和删除具有平均不变的时间复杂度。
在内部,元素不是按任何特定顺序排序的,而是bucket桶组织的。元素放入哪个bucket完全取决于其值的哈希。这允许对单个元素进行快速访问,因为一旦计算了哈希,它就引用了元素放入的确切bucket。
不能修改容器元素(即使是通过非常量迭代器),因为修改可能会更改元素的哈希并损坏容器。
# 2、stl 组成
stl组成包括算法,容器,迭代器, 仿函数,适配器,空间配置器 。
# 3、容器源码分析
## 3.1 list
分析以\# include 中的list进行分析,该list调用的是#include中的list
### 3.1.1 构造方法
```c++
list mlist(4,12);
__gnu_cxx::malloc_allocator all;
list> mlist2(8,12,all);
list> mlist3(mlist2);
```
list的构造方法有12个,默认为list().比较常用的构造方法有:
```c++
list()
list(const allocator_type& __a)
list(size_type __n, const allocator_type& __a = allocator_type())
list(size_type __n, const value_type& __value,const allocator_type& __a = allocator_type())
list(const list& __x)
list(const list& __x, const allocator_type& __a)
-a 指定的是分配器的种类
-n 指定的是容器初始化的大小
-value 指定的是默认的值
-x 复制已有的list
```
### 3.1.2 赋值运算
代码
**普通函数 > 特化的模板函数 > 模板函数**
```c++
#include
#include
# include
#include
using namespace std;
template void fuzhi(T &t1,T&& t2){
cout << "模板优先"< &t1,list && t2){
cout << "函数优先"< mlist(4,12);
list mlist2;
fuzhi(mlist2,move(mlist));
list::iterator it = mlist.begin();
*it = 14;
for(list::iterator it = mlist2.begin();it!=mlist2.end();it++){
cout<<*it<::iterator it = mlist.begin();it!=mlist.end();it++){
cout<<*it< list<_Tp, _Alloc>& list<_Tp, _Alloc>::
operator=(const list& __x)
{
if (this != std::__addressof(__x))
{
#if __cplusplus >= 201103L
if (_Node_alloc_traits::_S_propagate_on_copy_assign())
{
auto& __this_alloc = this->_M_get_Node_allocator();
auto& __that_alloc = __x._M_get_Node_allocator();
if (!_Node_alloc_traits::_S_always_equal()
&& __this_alloc != __that_alloc)
{
// replacement allocator cannot free existing storage
clear();
}
std::__alloc_on_copy(__this_alloc, __that_alloc);
}
#endif
_M_assign_dispatch(__x.begin(), __x.end(), __false_type());
}
return *this;
}
```
### 3.1.3 assign运算 (拷贝/替换/范围赋值)
```c++
#该例子会擦除mlist2中多余的部分
list mlist(4,12);
list mlist2(8,12);
mlist2.assign(mlist.begin(),mlist.end());
```
```c++
void
assign(size_type __n, const value_type& __val)
{ _M_fill_assign(__n, __val); }
void
assign(_InputIterator __first, _InputIterator __last)
{ _M_assign_dispatch(__first, __last, __false_type()); }
template
void
list<_Tp, _Alloc>::
_M_fill_assign(size_type __n, const value_type& __val)
{
iterator __i = begin();
for (; __i != end() && __n > 0; ++__i, --__n)
*__i = __val;
if (__n > 0)
insert(end(), __n, __val);
else
erase(__i, end());
}
template
template
void
list<_Tp, _Alloc>::
_M_assign_dispatch(_InputIterator __first2, _InputIterator __last2,
__false_type)
{
iterator __first1 = begin();
iterator __last1 = end();
for (; __first1 != __last1 && __first2 != __last2;
++__first1, ++__first2)
*__first1 = *__first2;
if (__first2 == __last2)
erase(__first1, __last1);
else
insert(__last1, __first2, __last2);
}
```
### 3.1.4 get_allocator() const _GLIBCXX_NOEXCEPT
获取分配器
### 3.1.5 其他运算
```
1 bool empty() //判断是否为空
2 size_type size() //返回当前list的元素个数
3 size_type max_size() //list的最大插入个数
4 void resize(size_type __new_size); //重新设置大小
5 //如果当前列表数据小于设置尺寸,则追加给定的_x
void resize(size_type __new_size, const value_type& __x);
6 void push_front(const value_type& __x) //前插
7 void emplace_front(_Args&&... __args) //前插
8 void pop_front() //后删
9 //指定位置插入
iterator emplace(const_iterator __position,_Args&&... __args);
iterator insert(const_iterator __position, value_type&& __x)
10 //擦除指定位置的数据,节点大小每删除一个就减小1
iterator erase(const_iterator__first,const_iterator __last)
void swap(list& __x) //列表之间交换
11 //__position是指当前列表插入的位置,将数据插入当前列表位置之前或者之后,并将之前的列表插过的元素清空
void splice(const_iterator __position,list&& __x,const_iterator __first,const_iterator __last) noexcept
12 void remove(const _Tp& __value);//删除指定的值
13 _Predicate是一个函数,删除满足条件的值
template
void remove_if(_Predicate);
14 void unique(); //去除重复的
15 void merge(list&& __x);//合并以及清空_X
16 void merge(list&& __x, _StrictWeakOrdering __comp);//归并合并,清空_x
17 void reverse() _GLIBCXX_NOEXCEPT //翻转
18 排序
template
void sort(_StrictWeakOrdering);
```
## 3.2 deque
双端队列,支持随机访问,因为重写了"[]"运算符。
deque的存储是一个个的连续块,这些连续块中包含一个个的元素。 如下所示,展示了deque的部分数据结构,从里面可以看到,连续块实际上就是**_Map_pointer**,而块实际上是**_Elt_pointer**,对于每个块,又有指向当前元素的指针**_M_cur**,以及当前块的头指针**_M_first**和尾指针**_M_last**,以及指向连续块管理中心的**_M_node**,在遍历时,如果当前指针浏览到末尾后,并且是向下遍历,**_M_node**就需要加1,遍历下一个中心。
```c++
_GLIBCXX_CONSTEXPR inline size_t
__deque_buf_size(size_t __size)
{ return (__size < _GLIBCXX_DEQUE_BUF_SIZE
? size_t(_GLIBCXX_DEQUE_BUF_SIZE / __size) : size_t(1)); }
template
struct _Deque_iterator
{
typedef _Deque_iterator<_Tp, _Tp&, _Tp*> iterator;
typedef _Deque_iterator<_Tp, const _Tp&, const _Tp*> const_iterator;
typedef _Tp* _Elt_pointer;
typedef _Tp** _Map_pointer;
typedef std::random_access_iterator_tag iterator_category;
typedef _Tp value_type;
typedef _Ptr pointer;
typedef _Ref reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef _Deque_iterator _Self;
_Elt_pointer _M_cur; //此迭代器所指之缓冲区中的现行(current)元素
_Elt_pointer _M_first; // 此迭代器所指之缓冲区的头
_Elt_pointer _M_last; // 此迭代器所指之缓冲区的尾(含备用空间)
_Map_pointer _M_node; // 指向管控中心
//初始化
_Deque_iterator(_Elt_pointer __x, _Map_pointer __y) _GLIBCXX_NOEXCEPT
: _M_cur(__x), _M_first(*__y),
_M_last(*__y + _S_buffer_size()), _M_node(__y) { }
_Deque_iterator() _GLIBCXX_NOEXCEPT
: _M_cur(), _M_first(), _M_last(), _M_node() { }
_Deque_iterator(const iterator& __x) _GLIBCXX_NOEXCEPT
: _M_cur(__x._M_cur), _M_first(__x._M_first),
_M_last(__x._M_last), _M_node(__x._M_node) { }
_Self&
operator++() _GLIBCXX_NOEXCEPT
{
++_M_cur;
if (_M_cur == _M_last)
{
_M_set_node(_M_node + 1);
_M_cur = _M_first;
}
return *this;
}
reference
operator*() const _GLIBCXX_NOEXCEPT
{ return *_M_cur; }
}
```
### 3.2.1 构造方法
总共有10个构造函数。常用的有:
```c++
__a 分配器
__vallue 填充的常数项
__n 初始化的节点个数
__x 复制deque
```
```c++
deque() : _Base() { }
deque(const allocator_type& __a)
deque(size_type __n, const value_type& __value,
const allocator_type& __a = allocator_type())
: _Base(__a, __n)
deque(const deque& __x) //浅拷贝
deque(deque&& __x)
deque(const deque& __x, const allocator_type& __a)
deque(deque&& __x, const allocator_type& __a);//拷贝的同时,清空__x
deque(_InputIterator __first, _InputIterator __last,
const allocator_type& __a = allocator_type())
```
从下面的代码可以看到,__n指的是申请的元素个数,通过元素求得块的个数,从代码里可以看到,块的个数是有可能多出来一块的。
```c++
_Deque_base()
: _M_impl()
{ _M_initialize_map(0); }
template
void
_Deque_base<_Tp, _Alloc>::
_M_initialize_map(size_t __num_elements)
{
const size_t __num_nodes = (__num_elements/ __deque_buf_size(sizeof(_Tp))
+ 1);
this->_M_impl._M_map_size = std::max((size_t) _S_initial_map_size,
size_t(__num_nodes + 2));
this->_M_impl._M_map = _M_allocate_map(this->_M_impl._M_map_size);
// For "small" maps (needing less than _M_map_size nodes), allocation
// starts in the middle elements and grows outwards. So nstart may be
// the beginning of _M_map, but for small maps it may be as far in as
// _M_map+3.
_Map_pointer __nstart = (this->_M_impl._M_map
+ (this->_M_impl._M_map_size - __num_nodes) / 2);
_Map_pointer __nfinish = __nstart + __num_nodes;
__try
{ _M_create_nodes(__nstart, __nfinish); }
__catch(...)
{
_M_deallocate_map(this->_M_impl._M_map, this->_M_impl._M_map_size);
this->_M_impl._M_map = _Map_pointer();
this->_M_impl._M_map_size = 0;
__throw_exception_again;
}
this->_M_impl._M_start._M_set_node(__nstart);
this->_M_impl._M_finish._M_set_node(__nfinish - 1);
this->_M_impl._M_start._M_cur = _M_impl._M_start._M_first;
this->_M_impl._M_finish._M_cur = (this->_M_impl._M_finish._M_first
+ __num_elements
% __deque_buf_size(sizeof(_Tp)));
}
```
### 3.2.2 函数以及重写符号
1. assign
可以看到,该函数同样用于拷贝,替换,以及范围赋值
```c++
void
assign(size_type __n, const value_type& __val)
{ _M_fill_assign(__n, __val); }
void
assign(_InputIterator __first, _InputIterator __last)
{ _M_assign_dispatch(__first, __last, __false_type()); }
template
void
_M_assign_dispatch(_InputIterator __first, _InputIterator __last,__false_type)
{
typedef typename std::iterator_traits<_InputIterator>
::iterator_category _IterCategory;
_M_assign_aux(__first, __last, _IterCategory());
}
template
template
void
deque<_Tp, _Alloc>::
_M_assign_aux(_InputIterator __first, _InputIterator __last,
std::input_iterator_tag)
{
iterator __cur = begin();
for (; __first != __last && __cur != end(); ++__cur, ++__first)
*__cur = *__first;
if (__first == __last)
_M_erase_at_end(__cur);
else
insert(end(), __first, __last);
}
```
2. 重写 ”=“
```c++
如果当前节点过多,就删除掉多余的,删除后再赋值。其他情况下,就直接插入
template
deque<_Tp, _Alloc>&
deque<_Tp, _Alloc>::
operator=(const deque& __x)
{
if (&__x != this)
{
const size_type __len = size();
if (__len >= __x.size()){
_M_erase_at_end(std::copy(__x.begin(), __x.end(),this->_M_impl._M_start));
}else
{
const_iterator __mid = __x.begin() + difference_type(__len);
std::copy(__x.begin(), __mid, this->_M_impl._M_start);
insert(this->_M_impl._M_finish, __mid, __x.end());
}
}
return *this;
}
deque&
operator=(deque&& __x) noexcept(_Alloc_traits::_S_always_equal())
{
using __always_equal = typename _Alloc_traits::is_always_equal;
_M_move_assign1(std::move(__x), __always_equal{});
return *this;
}
```
3. resize
如果有指定默认值,且新的节点个数大于原来的,就使用指定默认值,否则就使用类型的默认值。
```
void
resize(size_type __new_size)
{
const size_type __len = size();
if (__new_size > __len)
_M_default_append(__new_size - __len);
else if (__new_size < __len)
_M_erase_at_end(this->_M_impl._M_start
+ difference_type(__new_size));
}
void
resize(size_type __new_size, const value_type& __x)
{
const size_type __len = size();
if (__new_size > __len)
insert(this->_M_impl._M_finish, __new_size - __len, __x);
else if (__new_size < __len)
_M_erase_at_end(this->_M_impl._M_start
+ difference_type(__new_size));
}
```
4. shrink_to_fit
清楚超出容量的数据,但是不破坏容器的本身的容量
```
void
shrink_to_fit() noexcept
{
_M_shrink_to_fit();
}
```
5. 其他方法不再介绍
## 3.3 vector
*vector* 是向量类型,它可以容纳许多类型的数据,如若干个整数。vector的扩容方式是将旧的数组转移到新的数组里去,这种方式是比较耗费时间的。以push_back为例子。每次扩容至少比原来增加一倍。
```c++
void
push_back(value_type &&__x)
{
emplace_back(std::move(__x));
}
/////////////////////////////////////////////////////////
template
template
void
vector<_Tp, _Alloc>::
emplace_back(_Args&&... __args)
{
if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
{
_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
std::forward<_Args>(__args)...);
++this->_M_impl._M_finish;
}
else
_M_emplace_back_aux(std::forward<_Args>(__args)...); //如果容量不够,就开始扩容存储了
}
//////////////////////////////////////////////////////////
template
template
void
vector<_Tp, _Alloc>::
_M_emplace_back_aux(_Args&&... __args)
{
//计算需要扩容的长度,这里为1,最终返回的是扩容后的数组长度。
const size_type __len =
_M_check_len(size_type(1), "vector::_M_emplace_back_aux");
pointer __new_start(this->_M_allocate(__len));
pointer __new_finish(__new_start);
__try
{
_Alloc_traits::construct(this->_M_impl, __new_start + size(),
std::forward<_Args>(__args)...);
__new_finish = pointer();
__new_finish
= std::__uninitialized_move_if_noexcept_a
(this->_M_impl._M_start, this->_M_impl._M_finish,
__new_start, _M_get_Tp_allocator());
++__new_finish;
}
__catch(...)
{
if (!__new_finish)
_Alloc_traits::destroy(this->_M_impl, __new_start + size());
else
std::_Destroy(__new_start, __new_finish, _M_get_Tp_allocator());
_M_deallocate(__new_start, __len);
__throw_exception_again;
}
std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish,
_M_get_Tp_allocator());
_M_deallocate(this->_M_impl._M_start,
this->_M_impl._M_end_of_storage
- this->_M_impl._M_start);
this->_M_impl._M_start = __new_start;
this->_M_impl._M_finish = __new_finish;
this->_M_impl._M_end_of_storage = __new_start + __len;
}
///////////////////////////////////////////
/**
计算扩容后的数组长度,从这里可以看到,扩容后的长度至少为之前的1倍
*/
size_type
_M_check_len(size_type __n, const char *__s) const
{
if (max_size() - size() < __n)
__throw_length_error(__N(__s));
const size_type __len = size() + std::max(size(), __n);
return (__len < size() || __len > max_size()) ? max_size() : __len;
}
/////////////////////////////////
/**
返回当前数组长度
*/
size_type
size() const _GLIBCXX_NOEXCEPT
{
return size_type(this->_M_impl._M_finish - this->_M_impl._M_start);
}
```
## 3.3 array
实际上就是实现了一个数组,不过对于数组做了一些操作,非可变
```c++
template
struct __array_traits
{
typedef _Tp _Type[_Nm];
static constexpr _Tp&
_S_ref(const _Type& __t, std::size_t __n) noexcept
{ return const_cast<_Tp&>(__t[__n]); }
static constexpr _Tp*
_S_ptr(const _Type& __t) noexcept
{ return const_cast<_Tp*>(__t); }
};
```
## 3.4 queue
单端队列吧,先进先出,底层默认调用的是deque实现。
```
template >
class queue
{
public:
typedef typename _Sequence::value_type value_type;
typedef typename _Sequence::reference reference;
typedef typename _Sequence::const_reference const_reference;
typedef typename _Sequence::size_type size_type;
typedef _Sequence container_type;
protected:
/**
* 'c' is the underlying container. Maintainers wondering why
* this isn't uglified as per style guidelines should note that
* this name is specified in the standard, [23.2.3.1]. (Why?
* Presumably for the same reason that it's protected instead
* of private: to allow derivation. But none of the other
* containers allow for derivation. Odd.)
*/
_Sequence c;
public:
explicit
queue(const _Sequence& __c)
: c(__c) { }
explicit
queue(_Sequence&& __c = _Sequence())
: c(std::move(__c)) { }
template>
explicit
queue(const _Alloc& __a)
: c(__a) { }
template>
queue(const _Sequence& __c, const _Alloc& __a)
: c(__c, __a) { }
template>
queue(_Sequence&& __c, const _Alloc& __a)
: c(std::move(__c), __a) { }
template>
queue(const queue& __q, const _Alloc& __a)
: c(__q.c, __a) { }
template>
queue(queue&& __q, const _Alloc& __a)
: c(std::move(__q.c), __a) { }
#endif
```
## 3.5 set
set中,每种元素只能出现一次。
### 3.5.1 构造方法
set的构造方法有12种
```c++
template ,
typename _Alloc = std::allocator<_Key>>
class set
{
// concept requirements
typedef typename _Alloc::value_type _Alloc_value_type;
__glibcxx_class_requires(_Key, _SGIAssignableConcept)
__glibcxx_class_requires4(_Compare, bool, _Key, _Key,
_BinaryFunctionConcept)
__glibcxx_class_requires2(_Key, _Alloc_value_type, _SameTypeConcept)
public :
// typedefs:
//@{
/// Public typedefs.
typedef _Key key_type;
typedef _Key value_type;
typedef _Compare key_compare;
typedef _Compare value_compare;
typedef _Alloc allocator_type;
//@}
private:
typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template rebind<_Key>::other _Key_alloc_type;
//set的底层数据结构是基于红黑树的,可以看到,每个元素的identy实际就是key
typedef _Rb_tree,
key_compare, _Key_alloc_type>
_Rep_type;
_Rep_type _M_t; // Red-black tree representing set.
typedef __gnu_cxx::__alloc_traits<_Key_alloc_type> _Alloc_traits;
public:
//@{
/// Iterator-related typedefs.
typedef typename _Alloc_traits::pointer pointer;
typedef typename _Alloc_traits::const_pointer const_pointer;
typedef typename _Alloc_traits::reference reference;
typedef typename _Alloc_traits::const_reference const_reference;
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// DR 103. set::iterator is required to be modifiable,
// but this allows modification of keys.
typedef typename _Rep_type::const_iterator iterator;
typedef typename _Rep_type::const_iterator const_iterator;
typedef typename _Rep_type::const_reverse_iterator reverse_iterator;
typedef typename _Rep_type::const_reverse_iterator const_reverse_iterator;
typedef typename _Rep_type::size_type size_type;
typedef typename _Rep_type::difference_type difference_type;
//@}
set()
_GLIBCXX_NOEXCEPT_IF(
is_nothrow_default_constructible::value
&&is_nothrow_default_constructible::value)
: _M_t() {}
explicit set(const _Compare &__comp,
const allocator_type &__a = allocator_type())
: _M_t(__comp, _Key_alloc_type(__a)) {}
template
set(_InputIterator __first, _InputIterator __last)
: _M_t()
{
_M_t._M_insert_unique(__first, __last);
}
template
set(_InputIterator __first, _InputIterator __last,
const _Compare &__comp,
const allocator_type &__a = allocator_type())
: _M_t(__comp, _Key_alloc_type(__a))
{
_M_t._M_insert_unique(__first, __last);
}
set(const set &__x)
: _M_t(__x._M_t) {}
/////////////////////////////////////////////////////////////
template
struct _Identity
: public unary_function<_Tp,_Tp>
{
_Tp&
operator()(_Tp& __x) const
{ return __x; }
const _Tp&
operator()(const _Tp& __x) const
{ return __x; }
};
```
## 3.6 map
map因为底层也是调用的红黑树,并且存入红黑树的是,且以key作为排序的方式,所以不做介绍。
### 3.6.1 map与unordered_map的区别
map是基于红黑树,unordered_map是基于散列表。因此,在查询上,unordered_map会快一些,但是由于散列表性能不稳定,可能会有hash碰撞问题,导致查询时间为O(n)。插入数据上,map相对要快速一些,因为unordered_map中链表的插入是比较慢的。此外,在空间存储上,unordered_map要占的少一些,红黑树除了存储数据,还要存储左右子节点、父节点以及是否为黑节点。
```c++
#include
#include
#include