本文是我在学习候捷《STL标准库和泛型编程》课程时所做的笔记。在此分享给大家
STL源码剖析
命名空间
所有新式header的组件,都封装于“std"命名空间下。
STL六大件
容器(Container)、算法(Algorithm)、分配器(Allocator)、迭代器(Iterator)、适配器(Adapter)、仿函数(Functor)
关系
int ia[6] = {2, 52, 365, 37, 587, 23};
vector<int, allocator<int>> vi(ia, ia + 6);
cout << count_if(vi.begin(), vi.end(), not1(bind2nd(less<int>(), 40)));
//寻找范围内所有大于等于40的元素
vector
容器。allocator<int>
分配器。vi.begin()
迭代器。count_if()
算法。not1
仿函数适配器。less<int>()
仿函数。bind2nd(a,b)
适配器,绑定第二参数。not1()
适配器,否定相应内容。
not1(bind2nd(less<int>(), 40))
predicate,判断条件。
x.begin(),x.end()
维持的是左闭右开区间,x.end()
指向最后一个元素的下一位置。
迭代器遍历
Container<T> c;
for(Container<T>::iterator iter = c.begin(); iter!=c.end(); ++iter)
容器分类
红色部分为C++11新特性。
顺序型容器
array
只是将数组封装成class。
size()、front()、back()
可用
声明
array<type,SIZE> arr;
//必须声明类型及大小
arr.data() //起始地址
vector
变长数组(尾部可变)
deque
两端可扩。是分段连续的。
每段一个buffer,每个buffer可以存储一定的元素(满,进入下一个buffer),buffer两端可扩展。
list
双向链表。
forward-list
单链表。单链表没有push_back()
,而是push_front()
forward_list<int> fl;
fl.push_front(1);
fl.pop_front();
容器适配器
由deque封装而来。
queue
stack
关联式容器
key-value。通过key找value。查找更方便。
multi
key可重复。unordered
迭代器易失效。
set/multiset
底层红黑树。每个节点key即为value。
map/multimap
底层红黑树。除key之外有value。
unordered set/multiset
底层链式防冲突哈希表。每个节点key即为value。
unordered map/multimap
底层链式防冲突哈希表。除key之外有value。
分配器
在定义时候,带有默认值。可以不声明。以vector为例。
template <typename _Tp, typename _Alloc = std::allocator<_Tp>>
class vector : protected_Vector_base<_Tp, _Alloc>{
...............
}
OOP vs GP
面向对象编程(OOP
)企图将数据(data)和方法(method)关联一起。
泛型编程(GP
)企图将数据(data)和方法(method)分开。
GP可以将容器和算法各自分开开发,通过iterator连接即可。(操作符重载显得很重要)
template<typename _Tp, typename _Compare>
inline const _Tp&
max(const _Tp& __a, const _Tp& __b, _Compare __comp)
{
//return __comp(__a, __b) ? __b : __a;
if (__comp(__a, __b))
return __b;
return __a;
}
STL源码基础
运算符重载
:: . .* ?:
这四个运算符不能够重载
模板
主要用到的是类模板、函数模板。除此之外,还用到了特化。
特化之前要有template<>
分配器
operator new & malloc
内存分配的底层都是调用operator new
然后调用malloc
。通过malloc
,调用操作系统的api。
malloc所申请的内存,除所申请的内存外还有一些附加的额外开销。(因为释放时只传入指针,通过这些附加属性可以找到需要释放的地址。)
容器
set里面复合红黑树,非继承。两侧表示相应的sizeof
大小,至于容器中存的元素多少,与容器大小无关。
下图中缩进表示复合关系。
list
为了使迭代器更好的自增自减,一般均设计为类。
template<typename T>
struct list_node {
typedef void *void_pointer;
void_pointer prev;
void_pointer next;
T data;
};
template<typename T,class Alloc = alloc>
class list {
protected:
typedef list_node<T> list_node;
public:
typedef list_node *link_type;
typedef list_iterator<T, T &, T *> iterator;
private:
link_type node;
};
template<class T,class Ref,class Ptr>
struct list_iterator {
typedef list_iterator<T,Ref,Ptr> self;
typedef T value_type;
typedef Ptr pointer;
typedef Ref reference;
typedef list_node<T> *link_type;
//difference_type 和 iterator_category省略。
link_type node;
reference operator*() const {
return (*node).data;
}
pointer operator->() const {
return &(operator*());
}
}
其中需要特别注意的是,很多运算符已经重载,在阅读过程中要注意其功能及调用次序。
根据运算符的性质,考虑其返回值类型。(前置++传回引用,后置++传值)。
list底层
struct _List_node_base {
_List_node_base* _M_next;
_List_node_base* _M_prev;
};
struct _List_node : public _List_node_base {
_Tp _M_data;
}
struct _List_iterator_base {
_List_node_base* _M_node;
};
class _List_base
{
protected:
_List_node<_Tp>* _M_node;
};
class list : protected _List_base<_Tp, _Alloc> {
}
iterator遵循的原则
trait,萃取。
algorithm要知道iterator的一些属性。 iterator共五种associated type
。分别为(后面两种没有被使用过)
- iterator_category
- difference_type
- value_type
- reference
- pointer
以链表为例
template<typename _Tp>
struct _List_iterator
{
typedef _List_iterator<_Tp> _Self;
typedef _List_node<_Tp> _Node;
typedef ptrdiff_t difference_type;
typedef std::bidirectional_iterator_tag iterator_category;
typedef _Tp value_type;
typedef _Tp* pointer;
typedef _Tp& reference;
};
算法调用时,根据访问相应的类别,获取到相应的类型。
template <typename I>
algorithm(I first,I last) {
I::iterator_category
I::difference_type
I::value_type
I::pointer
I::reference
}
如果传入的iterator不是class(比如指针),通过萃取机(中间件)获得相应的类型。
- 间接询问
template<typename _Iterator>
struct iterator_traits
{
typedef typename _Iterator::iterator_category iterator_category;
typedef typename _Iterator::value_type value_type;
typedef typename _Iterator::difference_type difference_type;
typedef typename _Iterator::pointer pointer;
typedef typename _Iterator::reference reference;
};
- 偏特化
template<typename _Tp>
struct iterator_traits<_Tp*>
{
typedef random_access_iterator_tag iterator_category;
typedef _Tp value_type;
typedef ptrdiff_t difference_type;
typedef _Tp* pointer;
typedef _Tp& reference;
};
template<typename _Tp>
struct iterator_traits<const _Tp*>
{
typedef random_access_iterator_tag iterator_category;
typedef _Tp value_type;
//value_type主要用来声明变量
//如果是const,声明一个无法被赋值的变量,没有用。
typedef ptrdiff_t difference_type;
typedef const _Tp* pointer;
typedef const _Tp& reference;
};
vector
vector 内部封装了三个迭代器start
,finish
,end_of_storage
。
当容器填满时,在内存中分配另外一块两倍大小的空间。(造成迭代器失效)
deque
分段连续。内部有start
、finish
迭代器用来控制map
(控制中心)首尾,map
中的元素指向相应的buffer
。buffer
里存放具体的数据。为了维持连续这一假象,当触及到buffer
边界时,会扩充。除此之外,内部还有map_size
来判断大小。
__deque_buf_size
存在默认