侯捷 STL
源码之前
了无秘密
1.1 headers、版本、重要资源
- C++ Standard Library(标准库) vs. Standard Template Library(标准模板库)
2.2 STL 体系结构基础
-
六大部件:
- 容器(Container)
- 分配器(Allocators)
- 算法(Algorithms)
- 迭代器(Iterators)
- 适配器(Adapters)
- 仿函数(Functors)
#include<vector> #include<algorithm> #include<functional> #include<iostream> using namespace std; int main() { int ia[6] = {27, 210, 12, 47, 109, 83}; vector< int , allocator<int> > vi(ia,ia + 6); cout << count_if(vi.begin(), vi.end(), not1(bind2nd( less<int>(), 40 ))); return 0; }
-
复杂度,Complexity,Big-Oh
-
“前闭后开”区间
-
range-base for statement and auto (C++11)
3.3 容器 - 结构与分类(一)
- 大致上分为两种,一个叫做序列式(Sequence Containers),一种叫做关联式(Associative Containers),还有种一种 C++ 11的 Unordered Containers
4.4 容器 - 结构与分类(二)
- vector
5.5 & 6.6容 器 - 结构与分类 (三 & 四)
-
list / push_back / list.sort() /
-
list.sort()这样调用 sort 指的是调用 list 里面的 sort 排序而不是全局里面的排序,使用容器里面的 sort 一定比使用全局的 sort 要快
-
forward_list 单向链表(C++ 11)
-
slist 也是单向链表(GNU很早以前就有)
-
deque 双向开口两边都可进可出(了解 buffer 的概念,扩充的时候每次扩充一个buffer),deque 没有自己的 sort 和 find,使用功能得用 global 的
-
stack ,借用deque,容器的 adapter,常用的接口是 push ,和 pop
-
queue,借用deque,容器的 adapter,常用的接口是 push ,和 pop
-
由于 stack 和 queue 这两种容器没有自己的数据结构,它们是借用 deque 所以有人从技术上不把这两个容器叫作容器,叫作容器的 adapters
-
关联式,容器 multiset,insert,关联式容器的优点,红黑树做底部
-
multimap,key 和 value,红黑树做底部
-
unordered_multiset 哈希表做底部,放东西用 insert,入如果元素的个数大于篮子(bucket)的时候,篮子要扩充,所以篮子一定要比元素多,载重因子(load_factor)
-
unordered_multimap 同理
-
set 没有重复的元素
-
map key没有重复,value有重复
-
unordered_set
-
unordered_map
-
过去含有:
-
hash_set
-
hash_map
-
hash_multiset
-
hash_multimap
7.7 分配器(allocator)之测试
- 作者介绍的是使用 GNU C 的不同分配器,并对不同的分配器在容器中进行测试程式
- 可以脱离容器单用它的分配器来拿内存和还内存,不过这样确实没有必要,只是测试这个类可以用给你瞅瞅
- 一般我们在拿内存的时候使用 new 搭配 delete 或者 malloc 搭配 free,当然啊,这些最终归结起来都到了 malloc 搭配 free上去了
8.8 源代码之分布
9.9 OOP(Object-Oriented Programming) VS. GP(Generic Programming)
- OOP 企图将 datas 和 methods 关联在一起
- GP 是把 datas 和 methods 分开来
- list 不能像 vector 和 deque 一样用全局的 sort 来排序
10.10 技术基础:操作符重载 and 模板(泛化, 全特化, 偏特化)
- 泛化与(全)特化
- 偏特化(有两种,一种是个数的偏特化,一种是范围上的偏特化)
11.11 分配器(allocators)
-
扮演一个幕后英雄的角色,首先我们谈一谈 operator new () 和 malloc()
-
所有的 C++ 平台调用 operator new都含有 malloc
-
在VC6的allocator 只是以 ::operator new 和 ::operator delete 完成 allocate() 和 deallocate(),没有任何特殊设计
//自己调用的话(不建议)通常在容器完成 int* p = allocator<int>().allocate(512,(int*)0); allocator<int>().deallocate(p,512);
-
SGI 使用的是 alloc 而不是 allocator(因为它底层使用的 malloc 包装太多),malloc是给数据不同大小的需要设计的所以里面有cookie记录,但是容器中元素的的大小固定所以不需要
//用例(后来GNU 的版本改成之前的了,不知道为什么,但是仍然含有alloc,改为(__pool_alloc) vector<string,__gnu_cxx::__pool_alloc<string>> vec; //__gnu_cxx 在这个命名空间里面,源码里面可得
12.12 容器之间的实现关系与分类
- 各个容器之间的关系图
13.13 深度探索list(上)
-
可见书中 P129 页
-
GNU2.9 中的alloc分配器,因为list是非连续的,所以它的iterator不能是说是指针,要够聪明(够智能),所有的迭代器除了vector和array之外,他都要是一个类,这样他才能是一个智能指针含有
typedef __list_iterator<T,T&,T*> iterator;
-
C++不允许
i++++(即(i++)++)
,
14.14 深度探索list(下)
- 环状双向list,在最后一个元素后面还有一个end空白结点,便能够符合对于“前闭后开”区间的要求
- G2.9版是4大小,G4.9是8的原因
15.15*(重要)* 迭代器的设计原则和 Iterator Traits的作用与设计
-
Iterator 必须提供的5种 associated types(相关型别)
-
指针也是一种特化的Iterator,而Iterator 是一种泛化的指针,设计 traits 来区分是以class来表现的 Iterator 还是以指针的Iterator——相当于加上一个中介层来解决这个问题
-
typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
它是将
__type_traits<T>
这个模板类中的has_trivial_destructor
嵌套类型定义一个叫做trivial_destructor
的别名,清晰明了——typename的使用(很牛批)
16.16 vector 深度探索
- vector 的自动扩充,大家都默认是两倍成长
- 为什么insert_aux 函数还要多进行一次检查操作?因为把这个函数封装起来可能还有其他的容器可以调用
17.17 array 、forward_list 深度探索
18.18 deque 深度探索(上)
- deque 的 iterator(指出 node、first、last、current)
- deque是一个分段连续的,迭代器维持连续的假象,走到边界时有能力调到下一个buffer(缓冲区),至于cur 的作用是表示
- 几乎所有的容器都提供begin和end
19.19 deque 、queue、stack深度探索(下)
-
queue、stack都可以选择底层结构是deque和list
-
stack和queue不允许你随意放元素,所以都不允许遍历,不给你迭代器
-
stack和queue都不可以选择set 或map作为底层结构
20.20 RB-tree
- 关联式的容器查找、元素的安插都非常快
- 红黑树是平衡二元搜索树,提供遍历的功能,不应该使用红黑树的iterator来改变元素值
21.21 容器 set、multiset 深度探索
- 两者的底层结构都是 rb_tree,因此有元素自动排序的特点,排序的依据是 key,而set / multiset元素的value 和key 合一:value 就是 key
22.22 容器 map、multimap 深度探索
- map的“[]”的重载
23.23 & 24.24 容器 hashtable 深度探索
- 他比红黑树简单多,红黑树太复杂了,这个我可以讲的很精彩,hashtable 哈希表又叫 散列表
- hashtable 采用 Separate Chaining,链表越长,搜寻就越慢,bucket需要打散增加为两倍(质数),一般当元素个数超过bucket的个数,就得 rehashing,一般bucket为质数
- hash function(不唯一,够乱就好) 传出来的东西就是 hash code
- 标准库里面没有针对C++ 的字符串的hash function 有需要就得自己写一个
- 有了hash code之后,modulus运算(就是除了以后得到余数的运算)
25.25 hash_set、hash_multiset, hash_map,hash_multimap
- 这与之前所讲的区别是他们是以 hash table 为支撑的
26.26 unordered 容器概念
27.27 算法的形式
从语言层面上来说:
- 容器 container 是个 class template
- 算法 Algorithm 是个 function template
- 迭代器 Iterator 是个 class template
- 仿函数Functor 是个 class template
- 适配器Adapter 是个 class template
- 分配器 Allocator 是个 class template
28.28 迭代器的分类(P92)
29.29 iterator_category 对算法的影响(P99)
-
distance 和advance 的例子
-
Iterator_category 和 type trait 对算法的影响
-
泛化(generalization)特化(specialization)强化(refinement)
-
Iterator traits 与 Type Traits
-
算法源码中对iterator_category 的“暗示”
30.30 算法源代码剖析(11个例子)
34.34 not1
cout << count_if(vi.begin(), vi.end(),
not1(bind2nd( less<int>(), 40 )));
35.35 bind
-
std::bind 可以绑定
- functions
- function objects
- member functions
- data members
// bind example #include <iostream> // std::cout #include <functional> // std::bind // a function: (also works with function object: std::divides<double> my_divide;) double my_divide (double x, double y) {return x/y;} struct MyPair { double a,b; double multiply() {return a*b;} }; int main () { using namespace std::placeholders; // adds visibility of _1, _2, _3,... // binding functions: auto fn_five = std::bind (my_divide,10,2); // returns 10/2 std::cout << fn_five() << '\n'; // 5 auto fn_half = std::bind (my_divide,_1,2); // returns x/2 std::cout << fn_half(10) << '\n'; // 5 auto fn_invert = std::bind (my_divide,_2,_1); // returns y/x std::cout << fn_invert(10,2) << '\n'; // 0.2 auto fn_rounding = std::bind<int> (my_divide,_1,_2); // returns int(x/y) std::cout << fn_rounding(10,3) << '\n'; // 3 MyPair ten_two {10,2}; // binding members:member fuction 其实有一个argument:this auto bound_member_fn = std::bind (&MyPair::multiply,_1); // returns x.multiply() std::cout << bound_member_fn(ten_two) << '\n'; // 20 auto bound_member_data = std::bind (&MyPair::a,ten_two); // returns ten_two.a std::cout << bound_member_data() << '\n'; // 10 return 0; } vector<int> v{15,37,94,50,28,73,58,98}; int n = count_if(v.cbegin(),v.cend(),not1(bind2nd(less<int>(),50))); cout << "n= " << n <<endl; auto fn_ = bind(less<int>(),_1,50); cout << count_if(v.cbegin(),v.cend(),fn_) << endl; cout << count_if(v.cbegin(),v.cend(),bind(less<int>(),_1,50)) << endl;
36.36 reverse_iterator(迭代器适配器)
-
//迭代器适配器 reverse_iterator rbegin() {return reverse_iterator(end());} reverse_iterator rend() {return reverse_iterator(begin());}
-
里面有一个正向的迭代器,但实现的是反操作
37.37 insert
-
copy 的应用过程,insert 不用担心 copy 的不足
list<int> foo,bar; for(int i=1;i<5;i++) {foo.push_back(i);bar.push_back(i*10);} list<int>::iterator it = foo.begin(); advance(it,3);//不可以直接加,他是链表,不是顺序存储的 copy(bar.begin(),bar.end(),insert(foo,it));
38.38 ostream_iterator
- X 适配器:ostream_iterator,可以把迭代器绑定到一个装置上
39.39 istream_iterator
//istream_iterator example
#include<iostream>
#include<iterator>
int main()
{
double value1,value2;
std::cout << "please,insert two values:";
std::istream_iterator<double> iit(std::cin);
if(iit!=eos)vale2 = *iit;
++iit;
if(iit!=eos)value2=*iit;
std::cout << value1 << "*" <<value2<<"=" <<(value1*value2)<<"\n";
return 0;
}
40.40 一个万用的 hash function
41.41 Tuple 用例
42.42 & 43.43 Type traits
44.44 cout
- 是一个对象
45.45 & 46.46 movable元素对于deque速度效能的影响与测试代码
se,insert two values:";
std::istream_iterator iit(std::cin);
if(iit!=eos)vale2 = *iit;
++iit;
if(iit!=eos)value2=*iit;
std::cout << value1 << “" <<value2<<"=" <<(value1value2)<<”\n";
return 0;
}
## 40.40 一个万用的 hash function
## 41.41 Tuple 用例
## 42.42 & 43.43 Type traits
## 44.44 cout
- 是一个对象
## 45.45 & 46.46 movable元素对于deque速度效能的影响与测试代码
- 浅拷贝、深拷贝、move
- 写的有点粗糙,大家见谅,材料一枚,建议大家多多使用!
来源:CSDN
作者:面朝大海 & 春暖花开
链接:https://blog.csdn.net/qq_43517064/article/details/104069801