├── 0 SIG STL源码分析.md ├── 1 初次接触空间配置器.md ├── 10 uninitialized.md ├── 11 vector 上.md ├── 12 vector 中.md ├── 13 vector 下.md ├── 14 list 上.md ├── 15 list 中.md ├── 16 list 下.md ├── 17 deque 上.md ├── 18 deque 中.md ├── 19 deque 下.md ├── 2 第一级配置器.md ├── 20 stack.md ├── 21 queue.md ├── 22 heap.md ├── 23 priority_queue.md ├── 24 slist 上.md ├── 25 slist 下.md ├── 26 RB-tree 上.md ├── 27 RB-tree 下.md ├── 28 set.md ├── 29 pair.md ├── 3 第二级配置器.md ├── 30 map.md ├── 31 multiset.md ├── 32 multimap.md ├── 33 hashtable 上.md ├── 34 hashtable 下.md ├── 35 hash_set.md ├── 36 hash_multiset.md ├── 37 hash_map.md ├── 38 hash_multimap.md ├── 39 算法--copy.md ├── 4 内存池.md ├── 40 算法-rotate.md ├── 41 算法--数值算法.md ├── 42 算法-stl_algo.h基本算法.md ├── 43 算法-二分查找.md ├── 44 仿函数.md ├── 45 配接器.md ├── 5 迭代器.md ├── 6 模板中class与typename区别.md ├── 7 traits萃取剂.md ├── 8 全特化和偏特化.md ├── 9 __type_traits型别.md ├── README.md ├── assets ├── 特化1.png └── 特化2.png ├── template 4.md ├── template之模板注意事项.md ├── template之类相关.md └── template之非类型模板参数.md /0 SIG STL源码分析.md: -------------------------------------------------------------------------------- 1 | # SIG STL源码分析 2 | 3 | ## 前言 4 | 5 | 本专栏主要以STL源码剖析分析路线来分析SIGSTL3.0源码. 6 | 7 | 整个模块准备对学习`STL源码剖析`之后做一个系统的总结, 这些都是我个人的理解, 如果分析有什么问题欢迎各位大佬们指出. 也很感谢作者以及网络中各个大佬的总结, 让我也能更容易更深刻的理解到`STL`强大和方便, 也让我对`template`感受深刻. 8 | 9 | 以下是我自己对STL版块进行分析. 10 | 11 | **总共分为六个版块 : 空间配置器, 迭代器, 容器(序列容器, 关联容器), 算法, 仿函数, 配接器.** 12 | 13 | 14 | 15 | ## STL前期准备 16 | 17 | 在学习STL源码之前需要对`template`有一个认识或者回忆. 18 | 19 | [template(一)](https://blog.csdn.net/Function_Dou/article/details/84960611) 20 | 21 | [template(二)](https://blog.csdn.net/Function_Dou/article/details/84960644) 22 | 23 | [template(三)](https://blog.csdn.net/Function_Dou/article/details/84960661) 24 | 25 | ## STL分析 26 | 27 | 28 | 29 | ### 空间配置器 30 | 31 | c/c++都需要手动的管理内存, 而封装实现一个能申请空间又能自己释放空间不再让我们自己管理. 而STL就实现了这样的一个功能, 它就是`空间配置器`. 而空间配置器一般是隐藏在各个版块的组件, 实现中我们都看不到它的存在, 但是它确实是非常重要的部分, 因为它, 所有的版块操作内存时都直接调用它就行了, 而不需要再实现内存的分配. 32 | 33 | [0. new实现](https://blog.csdn.net/Function_Dou/article/details/84526761) 34 | 35 | [1. 空间配置器](https://blog.csdn.net/Function_Dou/article/details/84630781) 36 | 37 | [2. 第一级配置器](https://blog.csdn.net/Function_Dou/article/details/84631393) 38 | 39 | [3. 第二级配置器](https://blog.csdn.net/Function_Dou/article/details/84631714) 40 | 41 | [4. 内存池](https://blog.csdn.net/Function_Dou/article/details/84632272) 42 | 43 | 44 | 45 | ### 迭代器 46 | 47 | 每个组件都可能会涉及到对元素的简单操作, 比如 : 访问, 自增, 自减等. 每个组件都的数据类型可能不同, 所以每个组件可能都要自己设计对自己的操作. 但将每个组件的实现成统一的接口就是迭代器, 48 | 49 | 它的优点也很明显: 50 | 51 | 1. 它是能屏蔽掉底层数据类型差异的. 52 | 2. 迭代器将容器和算法粘合在一起, 使版块之间更加的紧凑, 同时提高了执行效率, 让算法更加的得到优化. 53 | 54 | 这些实现大都通过`traits`编程实现的. 它的定义了一个类型名规则, 满足`traits`编程规则就可以自己实现对`STL`的扩展, 也体现了`STL`的灵活性. 同时`straits`编程让程序根据不同的参数类型选择执行更加合适参数类型的处理函数, 也就提高了`STL`的执行效率. 可见迭代器对`STL`的重要性. 55 | 56 | [1. 迭代器](https://blog.csdn.net/Function_Dou/article/details/84639477) 57 | 58 | [2. template(四)](https://blog.csdn.net/Function_Dou/article/details/84644963) 59 | 60 | [3. traits萃取剂](https://blog.csdn.net/Function_Dou/article/details/84783915) 61 | 62 | [4. template(五)](https://blog.csdn.net/Function_Dou/article/details/84783953) 63 | 64 | [5. __type_traits型别](https://blog.csdn.net/Function_Dou/article/details/84784122) 65 | 66 | 67 | 68 | ### 容器 69 | 70 | 容器是封装了大量常用的数据结构, 因为容器, 凸显出STL的方便, 操作简单. 毕竟它将常用的但是实现比较麻烦的数据结构封装之后就可以直接的调用, 不再让用户重写一长串的代码实现. 71 | 72 | 容器根据排列分为了**序列式和关联式**. 73 | 74 | 1. 序列式包括`vector`, `list`, `deque`等. 序列容器有头或尾, 甚至有头有尾. 75 | 2. 关联式包括`map`, `set`, `hashtable`等. 关联容器没有所谓的头尾, 只有最大值, 最小值. 76 | 77 | 学习容器的时候要注意`end`返回的是**最后一元素的后一个地址, 这个地址并没有储存实际的值.** 78 | 79 | [0. uninitialized系列函数](https://blog.csdn.net/Function_Dou/article/details/84784188) 80 | 81 | ##### 序列容器 82 | 83 | [1. vector序列容器(一)](https://blog.csdn.net/Function_Dou/article/details/84784244) 84 | 85 | [2. vector序列容器(二)](https://blog.csdn.net/Function_Dou/article/details/84784317) 86 | 87 | [3. vector序列容器(三)](https://blog.csdn.net/Function_Dou/article/details/84784363) 88 | 89 | [4. list有序容器(一)](https://blog.csdn.net/Function_Dou/article/details/84798275) 90 | 91 | [5. list有序容器(二)](https://blog.csdn.net/Function_Dou/article/details/84798320) 92 | 93 | [6. list有序容器(三)](https://blog.csdn.net/Function_Dou/article/details/84798353) 94 | 95 | [7. deque有序容器(一)](https://blog.csdn.net/Function_Dou/article/details/84830206) 96 | 97 | [8. deque有序容器(二)](https://blog.csdn.net/Function_Dou/article/details/84830297) 98 | 99 | [9. deque有序容器(三)](https://blog.csdn.net/Function_Dou/article/details/84830391) 100 | 101 | [10. stack配接器](https://blog.csdn.net/Function_Dou/article/details/84830495) 102 | 103 | [11. queue配接器](https://blog.csdn.net/Function_Dou/article/details/84830597) 104 | 105 | [12. heap大根堆](https://blog.csdn.net/Function_Dou/article/details/84844886) 106 | 107 | [13. 优先级队列](https://blog.csdn.net/Function_Dou/article/details/84844960) 108 | 109 | [14. slist有序容器(一)](https://blog.csdn.net/Function_Dou/article/details/84845060) 110 | 111 | [15. slist有序容器(二)](https://blog.csdn.net/Function_Dou/article/details/84863153) 112 | 113 | 114 | 115 | ##### 关联容器 116 | 117 | [1. RB-tree关联容器(一)](https://blog.csdn.net/Function_Dou/article/details/84863185) 118 | 119 | [2. RB-tree关联容器(二)](https://blog.csdn.net/Function_Dou/article/details/84863205) 120 | 121 | [3. set配接器](https://blog.csdn.net/Function_Dou/article/details/84863323) 122 | 123 | [4. pair结构体](https://blog.csdn.net/Function_Dou/article/details/84863355) 124 | 125 | [5. map配接器](https://blog.csdn.net/Function_Dou/article/details/84863368) 126 | 127 | [6. multiset配接器](https://blog.csdn.net/Function_Dou/article/details/84864184) 128 | 129 | [7. multimap配接器](https://blog.csdn.net/Function_Dou/article/details/84864406) 130 | 131 | [8. hashtable关联容器(一)](https://blog.csdn.net/Function_Dou/article/details/84892823) 132 | 133 | [9. hashtable关联容器(二)](https://blog.csdn.net/Function_Dou/article/details/84892837) 134 | 135 | [10. hash_set配接器](https://blog.csdn.net/Function_Dou/article/details/84892849) 136 | 137 | [11. hash_multiset配接器](https://blog.csdn.net/Function_Dou/article/details/84892860) 138 | 139 | [12. hash_map配接器](https://blog.csdn.net/Function_Dou/article/details/84892887) 140 | 141 | [13. hash_multimap配接器](https://blog.csdn.net/Function_Dou/article/details/84892925) 142 | 143 | 144 | 145 | ### 算法 146 | 147 | STL的算法有很多, 有简单也有一些复杂的, 这里我就以`STL3.0`源码为例, 挑选出来几个常用的算法来进行分析. 148 | 149 | [1. copy算法](https://blog.csdn.net/Function_Dou/article/details/84892941) 150 | 151 | ### 仿函数 152 | 153 | 所谓仿函数也就是函数对象, 以前是这样称呼它的, 只是一直沿用至今了. **仿函数就是一种具有函数特质的对象**. STL因为仿函数, 大大在增加了灵活性, 而且可以将部分操作由用户自己来定义然后传入自定义的函数名就可以被调用. 但是你会发现仿函数实现的功能都是相当简单的, 而且都要通过配接器再封装才能够使用. 154 | 155 | STL的仿函数根据参数个数可以分为 : 一元仿函数和二元仿函数. 根据功能可分为 : 算术运算, 关系运算和逻辑运算. 156 | 157 | [1. 仿函数](https://blog.csdn.net/Function_Dou/article/details/84934052) 158 | 159 | 160 | 161 | ### 配接器 162 | 163 | 主要用来修改接口. 修改迭代器的接口, 修改容器的接口, 修改仿函数的接口. 164 | 165 | [1. 配接器](https://blog.csdn.net/Function_Dou/article/details/84934062) 166 | 167 | 168 | 169 | ## 总结 170 | 171 | STL这本书对我这个菜鸟收获还是挺有帮助的, 了解了`template`的强大, STL对程序做到了最大的优化. STL对每个功能都尽可能的单一, 然后可能通过多次的函数调用才调用真正执行的函数. 172 | 173 | 让我对c++有了一个更深的认识, 真的很难, 自己还差的太远, 努力加油! -------------------------------------------------------------------------------- /1 初次接触空间配置器.md: -------------------------------------------------------------------------------- 1 | # 初次接触空间配置器 2 | 3 | #### 前言 4 | 5 | SGI STL将new的申请空间和调用构造函数的两个功能分开实现, 如果对new不太清楚的, 可以先去看看这一篇[new实现](https://blog.csdn.net/Function_Dou/article/details/84526761)再来看配置器也不迟. 本节是STL分析的第一篇, 主要分析STL各个部分都会出现的`alloc`实现, 虽然每个部分都只会默认调用它, 不了解它也可以看懂分析, 但是他又是不可缺少的, 我们就以它做为开篇进行分析. 6 | 7 | 8 | 9 | #### "new"的实现 10 | 11 | 这里直接我们直接来看STL的`construct`实现吧 12 | 13 | ```c++ 14 | // 这里的construct调用的是placement new, 在一个已经获得的内存里建立一个对象 15 | template 16 | inline void construct(T1* p, const T2& value) 17 | { 18 | new (p) T1(value); 19 | } 20 | ``` 21 | 22 | 可以明白这里就只是一个`placement new`的调用, 只是用了泛型来实现一个对象分配的模板, 并实现初始化. 23 | 24 | 既然已经看到了对象的分配, 那再接再厉看看空间的分配, 充分了解STL是怎么将new分开执行的. allocate函数实现空间的申请, 但是这里有一点看不出来, 申请内存是有分为一级配置器和二级配置器, 分配的空间小于128字节的就调用二级配置器, 大于就直接使用一级配置器, 一级配置器直接调用`malloc`申请, 二级使用内存池. 25 | 26 | ```c++ 27 | template 28 | inline T* allocate(ptrdiff_t size, T*) 29 | { 30 | set_new_handler(0); 31 | T* tmp = (T*)(::operator new(size)(size * sizeof(T))); 32 | if(!tmp) 33 | { 34 | cerr << "out of memort" << endl; 35 | exit(1); 36 | } 37 | return tmp; 38 | } 39 | ``` 40 | 41 | 内存分配果然是调用`operator new`来执行空间分配, 这里allocate和construct都只是简单的对`operator new`进行封装. 42 | 43 | ```c++ 44 | const int N = 4; 45 | int main() 46 | { 47 | allocator alloc; 48 | auto str_ve = alloc.allocate(N); 49 | auto p = str_ve; // vector *p = str_ve; 50 | alloc.construct(p++); 51 | alloc.construct(p++, 10, 'a'); 52 | alloc.construct(p++, "construct"); 53 | cout << str_ve[0] << endl; 54 | cout << str_ve[1] << endl; 55 | cout << str_ve[2] << endl; 56 | 57 | while(p != str_ve) 58 | { 59 | alloc.destroy(--p); 60 | } 61 | alloc.deallocate(str_ve, N); 62 | 63 | exit(0); 64 | } 65 | ``` 66 | 67 | 输出结果为 68 | 69 | ```shell 70 | rpz@0505:stl3.0$ ./a.out 71 | 72 | aaaaaaaaaa 73 | construct 74 | ``` 75 | 76 | 这个程序首先调用`allocate`申请N个大小的空间, 在依次`construct`调用构造函数, 这里就先初始化3个结构, 紧接着通过`destory`调用析构函数, 最后`deallocate`释放申请的空间. 整个过程很容易理解, 但是这里还要深入是dealllocate和destroy两个函数. 77 | 78 | 79 | 80 | #### "delete"实现 81 | 82 | 先是看`destroy`调用析构函数. 而destroy有两个版本. 83 | 84 | **版本一:** 85 | 86 | 需要传入的参数 : **一个指针** 87 | 88 | ```c++ 89 | // 第一版本, 接收指针 90 | template inline void destroy(T* pointer) 91 | { 92 | pointer->~T(); 93 | } 94 | ``` 95 | 96 | 版本一直接就调用了析构函数, 不用过多的分析. 97 | 98 | 99 | 100 | **版本二:** 101 | 102 | 需要传入的参数 : **两个迭代器** 103 | 104 | ```c++ 105 | // 第二个版本的, 接受两个迭代器, 并设法找出元素的类型. 通过__type_trais<> 找出最佳措施 106 | template 107 | inline void destroy(ForwardIterator first, ForwardIterator last) 108 | { 109 | __destroy(first, last, value_type(first)); 110 | } 111 | 112 | // 接受两个迭代器, 以__type_trais<> 判断是否有traival destructor 113 | template 114 | inline void __destroy(ForwardIterator first, ForwardIterator last, T*) 115 | { 116 | typedef typename __type_traits::has_trivial_destructor trivial_destructor; 117 | __destroy_aux(first, last, trivial_destructor()); 118 | } 119 | ``` 120 | 121 | destroy直接调用`__destroy`, 前者只是一个接口, 所以重点是在后者. 122 | 123 | 分析`__type_traits<>` : 它是用于获取迭代器所指对象的类型,运用traits技法实现的.只要记住我们用来获取对对象类型就可以了. 然后通过类型的不一样选择执行不同的析构调用. 124 | 125 | 126 | 127 | 当`__type_traits `为`__false_type`时, 调用的是下面这个函数, 通过迭代所有的对象并调用版本一的函数执行析构函数进行析构. 而这个是被称为`non-travial destructor ` 128 | 129 | ```c++ 130 | // 没有non-travial destructor 131 | template 132 | inline void __destroy_aux(ForwardIterator first, ForwardIterator last, __false_type) 133 | { 134 | for ( ; first < last; ++first) 135 | destroy(&*first); 136 | } 137 | ``` 138 | 139 | 140 | 141 | 当`__type_traits `为`__true_type`时, 什么也不做, 因为这样效率很高效, 并不需要执行析构函数. 而这个是被称为`travial destructor `. 142 | 143 | ```c++ 144 | // 有travial destructor 145 | template 146 | inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type) {} 147 | ``` 148 | 149 | 150 | 151 | 最后是版本二的特化版, 同样也什么都不用做, 没有必要做析构. 152 | 153 | ```c++ 154 | inline void destroy(char*, char*) {} 155 | inline void destroy(wchar_t*, wchar_t*) {} 156 | ``` 157 | 158 | destroy分为这么几个版本和几个不同的函数执行都是为了提升效率, 较小的调用并不能看出什么, 但是如果是范围析构的话这样不同的选择析构能很节约时间和效率. 159 | 160 | 161 | 162 | 讲解完了destory后应该就能明白上面代码循环执行析构函数了. 163 | 164 | --- 165 | 166 | #### 小结 167 | 168 | 这里用一个小小的例子来理解"new"和"delete"运算符, 理解new, delete每步分开执行, 内存释放(deallocate)这里没有讲解, 也只是简单的调用free函数. STL这样做1. 为了效率, 2. 为了构建内存池. 169 | 170 | 最后将所有的函数进行封装到`allocator` , 所以例子中都是调用的构造析构等都是封装在该类中. 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /10 uninitialized.md: -------------------------------------------------------------------------------- 1 | # 内存处理工具 2 | 3 | [TOC] 4 | 5 | ---- 6 | 7 | 8 | 9 | ### 前言 10 | 11 | 这里将`内存处理工具`放到这里来讲是因为必须对`__type_traits`类型定义有所了解, 对`traits`编程有所了解才行.(如果你还没有了解这些, 希望能去看一下前面4篇的文章). 而且容器实现的很多地方都会用到`uninitialized_copy`的函数, 虽然乍一看功能应该是执行复制操作, 但如果第一次看或者事先没有理解该函数怎么工作的可能会影响到部分人分析代码的效率, 所以在这里就提前对其进行分析. 12 | 13 | 14 | 15 | ### uninitialized_copy函数 16 | 17 | `uninitialized_copy`功能 : 从first到last范围内的元素复制到从 result地址开始的内存. 18 | 19 | ```c++ 20 | template 21 | inline ForwardIterator uninitialized_copy(InputIterator first, InputIterator last, 22 | ForwardIterator result) { 23 | return __uninitialized_copy(first, last, result, value_type(result)); 24 | } 25 | ``` 26 | 27 | 很明显这就只是调用另一个函数的接口而已. 28 | 29 | `uninitialized_copy`类似于第一篇分析空间配置器的`destory`针对`const char*`和`const wchar*`单独做了特例化. 30 | 31 | ```c++ 32 | inline char* uninitialized_copy(const char* first, const char* last, 33 | char* result) { 34 | memmove(result, first, last - first); 35 | return result + (last - first); 36 | } 37 | 38 | inline wchar_t* uninitialized_copy(const wchar_t* first, const wchar_t* last, 39 | wchar_t* result) { 40 | memmove(result, first, sizeof(wchar_t) * (last - first)); 41 | return result + (last - first); 42 | } 43 | ``` 44 | 45 | 直接调用c++的`memmove`操作, 毕竟这样的效率更加的高效. 46 | 47 | 48 | 49 | #### __uninitialized_copy函数 50 | 51 | ```c++ 52 | template 53 | inline ForwardIterator __uninitialized_copy(InputIterator first, InputIterator last, 54 | ForwardIterator result, T*) { 55 | typedef typename __type_traits::is_POD_type is_POD; 56 | return __uninitialized_copy_aux(first, last, result, is_POD()); 57 | } 58 | ``` 59 | 60 | `__uninitialized_copy`使用了`typename`进行萃取, 并且萃取的类型是`POD`, 看来这里准备对`__uninitialized_copy` 进行最优化处理了, 我们接着来分析它是怎么实现优化处理的. 61 | 62 | 63 | 64 | #### __uninitialized_copy_aux优化处理 65 | 66 | ```c++ 67 | template 68 | inline ForwardIterator __uninitialized_copy_aux(InputIterator first, InputIterator last, 69 | ForwardIterator result, 70 | __true_type) { 71 | return copy(first, last, result); 72 | } 73 | 74 | template 75 | ForwardIterator __uninitialized_copy_aux(InputIterator first, InputIterator last, 76 | ForwardIterator result, 77 | __false_type) { 78 | ForwardIterator cur = result; 79 | __STL_TRY { 80 | for ( ; first != last; ++first, ++cur) 81 | construct(&*cur, *first); 82 | return cur; 83 | } 84 | __STL_UNWIND(destroy(result, cur)); 85 | } 86 | ``` 87 | 88 | `__uninitialized_copy`针对普通类型(int, double)做了特殊的优化, 以执行更高效的处理, 对类和用户定义类型做了构造处理, 当然用户自定义的不一定是类, 但是编译器为了安全性依然会执行最慢处理. 89 | 90 | 91 | 92 | ### uninitialized_copy_n函数 93 | 94 | `uninitialized_copy_n`也做了跟`uninitialized_copy`类似的处理, 只是它是采用`tratis`编程里`iterator_category`迭代器的类型来选择最优的处理函数. 95 | 96 | ````c++ 97 | template 98 | inline pair 99 | uninitialized_copy_n(InputIterator first, Size count, 100 | ForwardIterator result) { 101 | return __uninitialized_copy_n(first, count, result, iterator_category(first)); // 根据iterator_category选择最优函数 102 | } 103 | 104 | template 105 | pair 106 | __uninitialized_copy_n(InputIterator first, Size count, ForwardIterator result, 107 | input_iterator_tag) // input_iterator_tag类型的迭代器 108 | { 109 | ForwardIterator cur = result; 110 | __STL_TRY { 111 | for ( ; count > 0 ; --count, ++first, ++cur) 112 | construct(&*cur, *first); 113 | return pair(first, cur); 114 | } 115 | __STL_UNWIND(destroy(result, cur)); 116 | } 117 | 118 | template 119 | inline pair 120 | __uninitialized_copy_n(RandomAccessIterator first, Size count, ForwardIterator result, 121 | random_access_iterator_tag) // random_access_iterator_tag类型的迭代器 122 | { 123 | RandomAccessIterator last = first + count; 124 | return make_pair(last, uninitialized_copy(first, last, result)); 125 | } 126 | ```` 127 | 128 | 129 | 130 | ### uninitialized_fill函数 131 | 132 | `uninitialized_fill`功能 : 从first到last范围内的都填充为 x 的值. 133 | 134 | `uninitialized_fill`采用了与`uninitialized_copy`一样的处理方法选择最优处理函数, 这里就不过多的分析了. 135 | 136 | ```c++ 137 | template 138 | inline void uninitialized_fill(ForwardIterator first, ForwardIterator last, const T& x) 139 | { 140 | __uninitialized_fill(first, last, x, value_type(first)); 141 | } 142 | 143 | template 144 | inline void __uninitialized_fill(ForwardIterator first, ForwardIterator last, const T& x, T1*) 145 | { 146 | typedef typename __type_traits::is_POD_type is_POD; 147 | __uninitialized_fill_aux(first, last, x, is_POD()); 148 | 149 | } 150 | 151 | template 152 | inline void 153 | __uninitialized_fill_aux(ForwardIterator first, ForwardIterator last, const T& x, __true_type) 154 | { 155 | fill(first, last, x); 156 | } 157 | 158 | template 159 | void 160 | __uninitialized_fill_aux(ForwardIterator first, ForwardIterator last, const T& x, __false_type) 161 | { 162 | ForwardIterator cur = first; 163 | __STL_TRY { 164 | for ( ; cur != last; ++cur) 165 | construct(&*cur, x); 166 | } 167 | __STL_UNWIND(destroy(first, cur)); 168 | } 169 | ``` 170 | 171 | 172 | 173 | #### uninitialized_fill_n函数 174 | 175 | `uninitialized_fill_n`功能 : 从first开始n 个元素填充成 x 值. 176 | 177 | ```c++ 178 | template 179 | inline ForwardIterator uninitialized_fill_n(ForwardIterator first, Size n, const T& x) 180 | { 181 | return __uninitialized_fill_n(first, n, x, value_type(first)); 182 | } 183 | 184 | template 185 | inline ForwardIterator __uninitialized_fill_n(ForwardIterator first, Size n, const T& x, T1*) 186 | { 187 | typedef typename __type_traits::is_POD_type is_POD; 188 | return __uninitialized_fill_n_aux(first, n, x, is_POD()); 189 | } 190 | 191 | template 192 | inline ForwardIterator __uninitialized_fill_n_aux(ForwardIterator first, Size n, const T& x, __true_type) 193 | { 194 | return fill_n(first, n, x); 195 | } 196 | 197 | template 198 | ForwardIterator __uninitialized_fill_n_aux(ForwardIterator first, Size n, const T& x, __false_type) 199 | { 200 | ForwardIterator cur = first; 201 | __STL_TRY 202 | { 203 | for ( ; n > 0; --n, ++cur) 204 | construct(&*cur, x); 205 | return cur; 206 | } 207 | __STL_UNWIND(destroy(first, cur)); 208 | } 209 | ``` 210 | 211 | 212 | 213 | ### 总结 214 | 215 | `uninitialized_copy`是为两段内存进行复制的函数, `uninitialized_fill`是为对一段内存进行初始化一个值的函数. 两者都对了`traits`编程中的迭代器类型和`__type_traits`定义的`__false_type`和`__true_type`的不同执行不同的处理函数, 也使效率最优化. -------------------------------------------------------------------------------- /11 vector 上.md: -------------------------------------------------------------------------------- 1 | # vector 2 | 3 | ### 前言 4 | 5 | 在STL编程中, 容器就是我们经常会用到的, 容器分为序列容器和关联式容器. 而这一篇我们就分析序列容器之一`vector`. 关于用过`vector`三的人肯定对其一点都不陌生, `vector`基本能够支持任何类型的对象, 同时是一个可以动态增长数组. 马上就来分析关于`vector`是怎么实现这些功能. 6 | 7 | 8 | 9 | ### vector的简单调用 10 | 11 | 相信都对vector有一定的认识, 这里我就将本节会讲到关于`vector`源码的操作执行一次, 让大家先有一个回忆. 12 | 13 | ```c++ 14 | int main() 15 | { 16 | vector v1; 17 | vector v2(4); 18 | vector v3(4, 1); 19 | 20 | v1.push_back(1); 21 | v1.push_back(2); 22 | v1.push_back(3); 23 | v1.push_back(4); 24 | 25 | if(!v1.empty()) // 不为空 26 | { 27 | cout << *v1.begin() << " " << v1.front() << " " << *(v1.end() - 1) << " " << v1.back() << endl; 28 | cout << "size = " << v1.size() << endl; 29 | v1.~vector(); 30 | } 31 | 32 | exit(0); 33 | } 34 | ``` 35 | 36 | 输出: 37 | 38 | ```c++ 39 | 1 1 4 4 40 | size = 4 41 | ``` 42 | 43 | 回忆了vector最基本的操作, 现在我们就来分析其实现. 44 | 45 | 46 | 47 | ### vector容器 48 | 49 | #### vector基本数据结构 50 | 51 | `vector`为自己定义嵌套型别. 为了符合`traits`编程规则( 规则要求每个使用`traits`萃取器的都必须自己定义五个嵌套型别 ), 有一点很重要, `vector`迭代器就是一个普通指针.  普通类型可以有不同于其他类型的操作, 比如`uninitialized_copy`可以尽可能的优化执行等. 因为`vector`必须是存放一个连续的现行空间中, 并且连续空间的大小都要比用户自己要求的空间要大两倍, 剩余的空间是留着作备用, 毕竟`vector`可是能动态增长的, 所以留着部分空间以备增长. 这里也可以发现`vector`很大的问题, 当以备空间也用完之后, `vector`需要重新更大的申请空间然后释放掉之前的空间, 这样的代价也很大啊, 但是为了动态增长, 这点问题我们也可以接受的. 52 | 53 | ```c++ 54 | template 55 | class vector 56 | { 57 | public: 58 | // 定义vector自身的嵌套型别 59 | typedef T value_type; 60 | typedef value_type* pointer; 61 | typedef const value_type* const_pointer; 62 | // 定义迭代器, 这里就只是一个普通的指针 63 | typedef value_type* iterator; 64 | typedef const value_type* const_iterator; 65 | typedef value_type& reference; 66 | typedef const value_type& const_reference; 67 | typedef size_t size_type; 68 | typedef ptrdiff_t difference_type; 69 | ... 70 | protected: 71 | typedef simple_alloc data_allocator; // 设置其空间配置器 72 | iterator start; // 使用空间的头 73 | iterator finish; // 使用空间的尾 74 | iterator end_of_storage; // 可用空间的尾 75 | ... 76 | }; 77 | ``` 78 | 79 | 因为`vector` 需要表示用户的数据的起始地址, 结束地址, 还需要其真正的最大地址, 所以总共需要3个迭代器分别指向数据的头(start), 数据的尾(finish), 数组的尾(end_of_storage). 80 | 81 | ```c++ 82 | iterator start; // 使用空间的头 83 | iterator finish; // 使用空间的尾 84 | iterator end_of_storage; // 可用空间的尾 85 | ``` 86 | 87 | 88 | 89 | #### 构造函数 90 | 91 | `vector`有多个构造函数, 为了满足多种初始化. 92 | 93 | ```c++ 94 | vector() : start(0), finish(0), end_of_storage(0) {} // 默认构造函数 95 | explicit vector(size_type n) { fill_initialize(n, T()); } // 必须显示的调用这个构造函数, 接受一个值 96 | vector(size_type n, const T& value) { fill_initialize(n, value); } // 接受一个大小和初始化值. int和long都执行相同的函数初始化 97 | vector(int n, const T& value) { fill_initialize(n, value); } 98 | vector(long n, const T& value) { fill_initialize(n, value); } 99 | vector(const vector& x); // 接受一个vector参数的构造函数 100 | ``` 101 | 102 | ```c++ 103 | // 初始化vector的使用空间头和空间的尾 104 | void fill_initialize(size_type n, const T& value) 105 | { 106 | start = allocate_and_fill(n, value); // 初始化并初始化值 107 | finish = start + n; 108 | end_of_storage = finish; 109 | } 110 | ``` 111 | 112 | **初始化满足要么都初始化成功, 要么一个都不初始化并释放掉抛出异常** 113 | 114 | ```c++ 115 | // 调用默认的第二配置器分配内存, 分配失败就释放所分配的内存 116 | iterator allocate_and_fill(size_type n, const T& x) 117 | { 118 | iterator result = data_allocator::allocate(n); // 申请n个元素的线性空间. 119 | __STL_TRY // 对整个线性空间进行初始化, 如果有一个失败则删除全部空间并抛出异常. 120 | { 121 | uninitialized_fill_n(result, n, x); 122 | return result; 123 | } 124 | __STL_UNWIND(data_allocator::deallocate(result, n)); 125 | } 126 | ``` 127 | 128 | `vector`有一个接受vector参数的构造函数, 调用的是`uninitialized_copy`执行初始化, 我们在上一篇分析过该函数. 129 | 130 | ```c++ 131 | vector(const vector& x) 132 | { 133 | start = allocate_and_copy(x.end() - x.begin(), x.begin(), x.end()); 134 | finish = start + (x.end() - x.begin()); // 初始化头和尾迭代器位置 135 | end_of_storage = finish; 136 | } 137 | // 同样进行初始化 138 | template 139 | iterator allocate_and_copy(size_type n, ForwardIterator first, ForwardIterator last) 140 | { 141 | iterator result = data_allocator::allocate(n); 142 | __STL_TRY 143 | { 144 | uninitialized_copy(first, last, result); // 这里采用的是uninitialized_copy, 进行复制. 145 | return result; 146 | } 147 | __STL_UNWIND(data_allocator::deallocate(result, n)); 148 | } 149 | 150 | #else /* __STL_MEMBER_TEMPLATES */ 151 | // 支持两个迭代器表示范围的复制 152 | iterator allocate_and_copy(size_type n, 153 | const_iterator first, const_iterator last) 154 | { 155 | iterator result = data_allocator::allocate(n); 156 | __STL_TRY { 157 | uninitialized_copy(first, last, result); 158 | return result; 159 | } 160 | __STL_UNWIND(data_allocator::deallocate(result, n)); 161 | } 162 | #endif /* __STL_MEMBER_TEMPLATES */ 163 | }; 164 | ``` 165 | 166 | **构造函数, 接受两个迭代器, 构造一个范围的数据.** 167 | 168 | ```c++ 169 | #ifdef __STL_MEMBER_TEMPLATES 170 | template 171 | vector(InputIterator first, InputIterator last) : 172 | start(0), finish(0), end_of_storage(0) 173 | { 174 | range_initialize(first, last, iterator_category(first)); 175 | } 176 | #else /* __STL_MEMBER_TEMPLATES */ 177 | vector(const_iterator first, const_iterator last) { 178 | size_type n = 0; 179 | distance(first, last, n); 180 | start = allocate_and_copy(n, first, last); 181 | finish = start + n; 182 | end_of_storage = finish; 183 | } 184 | #endif /* __STL_MEMBER_TEMPLATES */ 185 | ``` 186 | 187 | 188 | 189 | #### 析构函数 190 | 191 | 析构函数就是直接调用`deallocate` 空间配置器, 从头释放到数据尾部, 最后将内存还给空间配置器. 192 | 193 | `vector`因为是类, 所以我们并不需要手动的释放内存, 生命周期结束后就自动调用析构从而释放调用空间, 当然我们也可以直接调用析构函数释放内存. 194 | 195 | ```c++ 196 | void deallocate() 197 | { 198 | if (start) 199 | data_allocator::deallocate(start, end_of_storage - start); 200 | } 201 | // 调用析构函数并释放内存 202 | ~vector() 203 | { 204 | destroy(start, finish); 205 | deallocate(); 206 | } 207 | ``` 208 | 209 | 210 | 211 | ### 属性获取 212 | 213 | 位置参数的获取. 因为`end()`返回的是`finish`, 而`finish`是指向最后一个元素的后一个位置的指针, 所以使用end的时候尽量注意一点. 214 | 215 | ```c++ 216 | public: 217 | // 获取数据的开始以及结束位置的指针. 记住这里返回的是迭代器, 也就是vector迭代器就是该类型的指针. 218 | iterator begin() { return start; } 219 | iterator end() { return finish; } 220 | // 获取值 221 | reference front() { return *begin(); } 222 | reference back() { return *(end() - 1); } 223 | // 获取右值 224 | const_iterator begin() const { return start; } 225 | const_iterator end() const { return finish; } 226 | const_reference front() const { return *begin(); } 227 | const_reference back() const { return *(end() - 1); } 228 | // 获取基本数组信息 229 | size_type size() const { return size_type(end() - begin()); } // 数组元素的个数 230 | size_type max_size() const { return size_type(-1) / sizeof(T); } // 最大能存储的元素个数 231 | size_type capacity() const { return size_type(end_of_storage - begin()); } // 数组的实际大小 232 | ``` 233 | 234 | 判断vector是否为空, 并不是比较元素为0, 是直接比较头尾指针. 235 | 236 | ```c++ 237 | bool empty() const { return begin() == end(); } 238 | ``` 239 | 240 | 241 | 242 | ### 总结 243 | 244 | 本节中我们主要分析了关于`vector`满足`traits`编程规范的定义, 构造函数, 析构函数, 数组的基本信息获取函数, 将对`vector`的直接操作放到下一节进行分析. 245 | 246 | 最后回忆一下本节的重要点 : 247 | 248 | 1. `vector`的迭代器是一个普通的指针 249 | 2. 构造函数的重载满足不同的用户需求 250 | 3. `vector`因为是类, 所以在生命周期结束后会自动调用析构函数, 用户不再手动的释放内存, 也不会出现内存泄露的问题, 用户也可以主动调用析构函数释放内存 251 | 4. finish是指向最后一个元素的后一位地址, 不是直接指向最后一个元素. -------------------------------------------------------------------------------- /12 vector 中.md: -------------------------------------------------------------------------------- 1 | # vector 2 | 3 | ## 前言 4 | 5 | 上一节我们分析了`vector`的构造, 析构, back, front等基本操作, 这一节我们就来分析`vector`实现插入, 删除等直接对数组具体操作的实现. 6 | 7 | 8 | 9 | ## vector实例 10 | 11 | 与上节一样, 我们将待会会用到的部分常用的操作先执行一次, 进行一次快速的回忆. 12 | 13 | ```c++ 14 | int main() 15 | { 16 | vector v1; 17 | vector v2(4); 18 | vector v3(4, 1); 19 | 20 | v1.push_back(1); 21 | v1.push_back(2); 22 | v1.push_back(3); 23 | v1.push_back(4); 24 | v1.push_back(5); 25 | 26 | // 这里清除的是一个[v1.begin()+2, v1.end()-1) , 左闭右开的区间 27 | v1.erase(v1.begin()+2, v1.end()-1); 28 | v1.pop_back(); 29 | 30 | v2 = v1; 31 | if(!v2.empty()) // 不为空 32 | { 33 | for (const auto i : v2) 34 | { 35 | cout << i << " "; 36 | } 37 | cout << endl; 38 | cout << "size = " << v2.size() << endl; 39 | v1.~vector(); 40 | } 41 | 42 | exit(0); 43 | } 44 | ``` 45 | 46 | 47 | 48 | ## vector实现 49 | 50 | ### push和pop操作 51 | 52 | **`vector`的push和pop操作都只是对尾进行操作.** 这里说的尾部是指数据的尾部, 尾部时数组大小的尾部. 53 | 54 | `push_back`从尾部插入数据. 当数组还有备用空间的时候就直接插入尾部就行了, 当没有备用空间后就重新寻找更大的空间再将数据全部复制过去. 55 | 56 | ```c++ 57 | // 如果可用空间还有就调用对象的构造函数并使用空间的尾增加 58 | // 没有空间就重新申请一个更大的空间, 然后进行插入 59 | void push_back(const T& x) 60 | { 61 | // 如果还没有到填满整个数组, 就在数据尾部插入 62 | if (finish != end_of_storage) 63 | { 64 | construct(finish, x); 65 | ++finish; 66 | } 67 | // 数组被填充满, 调用insert_aux必须重新寻找新的更大的连续空间, 再进行插入 68 | else 69 | insert_aux(end(), x); 70 | } 71 | ``` 72 | 73 | `pop_back`从尾部进行删除 74 | 75 | ```c++ 76 | // 使用空间的尾自减并调用其析构函数. 但是并没有释放内存 77 | void pop_back() 78 | { 79 | --finish; 80 | destroy(finish); 81 | } 82 | ``` 83 | 84 | push和pop也保证 了`finish`始终都指向最后一个元素的后一个位置的地址. 85 | 86 | 87 | 88 | ### 删除元素 89 | 90 | #### erase删除一个元素 91 | 92 | `erase`清除指定位置的元素, 其重载函数用于清除一个范围内的所有元素. 实际实现就是将删除元素后面所有元素往前移动, 对于vector来说这样的操作花费的时间还是很大的, 毕竟他是一个数组. 93 | 94 | ```c++ 95 | // 清除指定位置的元素. 实际就是将指定位置后面的所有元素向前移动, 最后析构掉最后一个元素 96 | iterator erase(iterator position) 97 | { 98 | if (position + 1 != end()) 99 | copy(position + 1, finish, position); 100 | --finish; 101 | destroy(finish); 102 | return position; 103 | } 104 | 105 | // 清除一个指定范围的元素, 同样将指定范围后面的所有元素向前移动, 最后析构掉整个范围的元素 106 | // 清除的是左闭右开的区间 [ ) 107 | iterator erase(iterator first, iterator last) 108 | { 109 | iterator i = copy(last, finish, first); 110 | destroy(i, finish); 111 | finish = finish - (last - first); 112 | return first; 113 | } 114 | ``` 115 | 116 | #### clear 117 | 118 | `clear`清除一个范围的数据. 119 | 120 | ```c++ 121 | void clear() { erase(begin(), end()); } 122 | ``` 123 | 124 | 125 | 126 | ### 重载运算符 127 | 128 | `vector`为实现给用户最直接最方便的用途, 重载了`[]`, `=`等运算符, 用户更加容易的能操作迭代器, 使用起来更像是直接操作数组一样. 129 | 130 | 131 | 132 | #### 重载 133 | 134 | `[]`返回的是元素的**引用, 即一个左值**, 毕竟可能会对元素值进行修改. 135 | 136 | ```c++ 137 | reference operator[](size_type n) { return *(begin() + n); } 138 | const_reference operator[](size_type n) const { return *(begin() + n); } 139 | ``` 140 | 141 | 针对右值和const类型选择不同的重载方法. 142 | 143 | vector的`[]`重载很有意思, 是`begin() + n`实现, 也就是说**n可以为负数**. 144 | 145 | ```c++ 146 | // 验证为n可以为负数 147 | int main() 148 | { 149 | vector a(10); 150 | iota(a.begin(), a.end(), 1); 151 | for_each(a.begin(), a.end(), [](int a){ cout << a << " ";}); // 1 2 3 4 5 6 7 8 9 10 152 | auto i = a.begin() + 3; 153 | cout << i[-1] << " " << a.end()[-1]; // 3 10 154 | 155 | exit(0); 156 | } 157 | ``` 158 | 159 | *end()[-1]可以这样的操作来获取最后一个元素, 当然这样的操作一般也不会这样干*. 160 | 161 | 162 | 163 | `vector`之间能相互的复制主要它也重载了`=`, 使相互传递更加的便利. 164 | 165 | ```c++ 166 | vector& operator=(const vector& x); 167 | template 168 | vector& vector::operator=(const vector& x) 169 | { 170 | if (&x != this) 171 | { 172 | // 判断x的数据大小跟赋值的数组大小 173 | if (x.size() > capacity()) // 数组大小过小 174 | { 175 | // 进行范围的复制, 并销毁掉原始的数据. 176 | iterator tmp = allocate_and_copy(x.end() - x.begin(), x.begin(), x.end()); 177 | destroy(start, finish); 178 | deallocate(); 179 | // 修改偏移 180 | start = tmp; 181 | end_of_storage = start + (x.end() - x.begin()); 182 | } 183 | // 数组的元素大小够大, 直接将赋值的数据内容拷贝到新数组中. 并将后面的元素析构掉 184 | else if (size() >= x.size()) 185 | { 186 | iterator i = copy(x.begin(), x.end(), begin()); 187 | destroy(i, finish); 188 | } 189 | // 数组的元素大小不够, 装不完x的数据, 但是数组本身的大小够大 190 | else 191 | { 192 | // 先将x的元素填满原数据大小 193 | copy(x.begin(), x.begin() + size(), start); 194 | // 再将x后面的数据全部填充到后面 195 | uninitialized_copy(x.begin() + size(), x.end(), finish); 196 | } 197 | finish = start + x.size(); 198 | } 199 | return *this; 200 | } 201 | ``` 202 | 203 | 我不清楚为什么最后一个实现需要分两步走, 既然原数组的大小够大, 元素小, 所以可以直接覆盖掉原数据就行了, 不清相分两步. 204 | 205 | 206 | 207 | ### 容器的调整 208 | 209 | `reserver`修改容器实际的大小 210 | 211 | ```c++ 212 | void reserve(size_type n) 213 | { 214 | // 修改的容器大小要大于原始数组大小才行 215 | if (capacity() < n) 216 | { 217 | const size_type old_size = size(); 218 | // 重新拷贝数据, 并将原来的空间释放掉 219 | iterator tmp = allocate_and_copy(n, start, finish); 220 | destroy(start, finish); 221 | deallocate(); 222 | // 重新修改3个迭代器位置 223 | start = tmp; 224 | finish = tmp + old_size; 225 | end_of_storage = start + n; 226 | } 227 | } 228 | ``` 229 | 230 | `resize`重新修改数组元素的容量. 这里是修改容纳元素的大小, 不是数组的大小. 231 | 232 | ```c++ 233 | void resize(size_type new_size) { resize(new_size, T()); } 234 | void resize(size_type new_size, const T& x) 235 | { 236 | // 元素大小大于了要修改的大小, 则释放掉超过的元素 237 | if (new_size < size()) 238 | erase(begin() + new_size, end()); 239 | // 元素不够, 就从end开始到要求的大小为止都初始化x 240 | else 241 | insert(end(), new_size - size(), x); 242 | } 243 | ``` 244 | 245 | 246 | 247 | ### swap交换 248 | 249 | vector实现`swap`就只是将3个迭代器进行交换即可, 并不用将整个数组进行交换. 250 | 251 | ```c++ 252 | void swap(vector& x) 253 | { 254 | __STD::swap(start, x.start); 255 | __STD::swap(finish, x.finish); 256 | __STD::swap(end_of_storage, x.end_of_storage); 257 | } 258 | ``` 259 | 260 | 261 | 262 | ## 总结 263 | 264 | 本节将`vector`的删除, 交换, 重载等操作进行的分析. 学到关于交换数组可以修改头尾指针即可, 并不实际交换整个元素. 同时要注意`erase`清除是一个**左闭右开的区间**. 因为insert的代码很多, 所以我将插入操作放到下节进行分析. 265 | -------------------------------------------------------------------------------- /13 vector 下.md: -------------------------------------------------------------------------------- 1 | # vector 2 | 3 | ## 前言 4 | 5 | 前面两节我们分析了关于`vector`的push, pop, erase, 重载和vector的定义, 通过前面的分析都对vector的实现有所了解了, 这一节我们就来分析vector是怎么实现插入操作. 当数组的大小不足后`insert`又是怎么来处理的. 6 | 7 | 8 | 9 | ## 插入实现 10 | 11 | `insert`为了接受不同的参数和参数个数, 所以定义了多个重载函数. 现在我们就一个个来进行分析. 12 | 13 | 14 | 15 | **insert(iterator position, const T& x) ** 传入一个迭代器和插入的值. 16 | 17 | 1. 如果数组还有备用空间, 并且插入的是finish位置, 直接插入即可, 最后调整finish就行了. 18 | 2. 以上条件不成立, 调用`insert_aux`函数执行插入操作 19 | 20 | ```c++ 21 | iterator insert(iterator position) { return insert(position, T()); } 22 | iterator insert(iterator position, const T& x) ; 23 | 24 | iterator insert(iterator position, const T& x) 25 | { 26 | size_type n = position - begin(); 27 | // 如果数组还有备用空间, 并且插入的是finish位置, 直接插入即可, 最后调整finish就行了. 28 | if (finish != end_of_storage && position == end()) 29 | { 30 | construct(finish, x); 31 | ++finish; 32 | } 33 | // 以上条件不成立, 调用另一个函数执行插入操作 34 | else 35 | insert_aux(position, x); 36 | return begin() + n; 37 | } 38 | ``` 39 | 40 | 1. **如果数组还有备用空间, 就直接移动元素, 再将元素插入过去, 最后调整finish就行了.** 41 | 2. **没有备用空间, 重新申请空间原始空间的两倍+1的空间后, 再将元素拷贝过去同时执行插入操作** 42 | 3. 析构调用原始空间元素以及释放空间, 最后修改3个迭代器的指向. 43 | 44 | ```c++ 45 | void insert_aux(iterator position, const T& x); 46 | 47 | template 48 | void vector::insert_aux(iterator position, const T& x) 49 | { 50 | // 如果数组还有备用空间, 就直接移动元素, 再将元素插入过去, 最后调整finish就行了. 51 | if (finish != end_of_storage) 52 | { 53 | // 调用构造, 并将最后一个元素复制过去, 调整finish 54 | construct(finish, *(finish - 1)); 55 | ++finish; 56 | T x_copy = x; 57 | // 将插入元素位置的后面所有元素往后移动, 最后元素插入到位置上. 58 | copy_backward(position, finish - 2, finish - 1); 59 | *position = x_copy; 60 | } 61 | // 没有备用空间, 重新申请空间再将元素拷贝过去同时执行插入操作 62 | else { 63 | const size_type old_size = size(); 64 | const size_type len = old_size != 0 ? 2 * old_size : 1; // 重新申请空间原始空间的两倍+1的空间 65 | 66 | iterator new_start = data_allocator::allocate(len); 67 | iterator new_finish = new_start; 68 | __STL_TRY { 69 | // 进行分段将原始元素拷贝新的空间中, 这样也就实现了插入操作 70 | new_finish = uninitialized_copy(start, position, new_start); 71 | construct(new_finish, x); 72 | ++new_finish; 73 | new_finish = uninitialized_copy(position, finish, new_finish); 74 | } 75 | 76 | # ifdef __STL_USE_EXCEPTIONS 77 | catch(...) { 78 | destroy(new_start, new_finish); 79 | data_allocator::deallocate(new_start, len); 80 | throw; 81 | } 82 | # endif /* __STL_USE_EXCEPTIONS */ 83 | // 释放掉原来的空间, 调整新的3个迭代器的位置 84 | destroy(begin(), end()); 85 | deallocate(); 86 | start = new_start; 87 | finish = new_finish; 88 | end_of_storage = new_start + len; 89 | } 90 | } 91 | ``` 92 | 93 | 94 | 95 | **void insert (iterator pos, size_type n, const T& x)** 传入一个迭代器, 插入的个数, 和插入的值. 共3个参数. 96 | 97 | 其重载函数都调用同一个函数实现插入操作. 98 | 99 | ```c++ 100 | void insert (iterator pos, size_type n, const T& x); 101 | void insert (iterator pos, int n, const T& x) 102 | { 103 | insert(pos, (size_type) n, x); 104 | } 105 | void insert (iterator pos, long n, const T& x) 106 | { 107 | insert(pos, (size_type) n, x); 108 | } 109 | ``` 110 | 111 | `insert(iterator position, size_type n, const T& x)`实现步骤: 112 | 113 | 1. 如果备用空间足够大, 先保存插入位置到end的距离 114 | 115 | 1. 从插入的位置到end的距离大于要插入的个数n 116 | 1. 先构造出finish-n个大小的空间, 再移动finish - n个元素的数据 117 | 2. 在将从插入位置后的n个元素移动 118 | 3. 最后元素从插入位置开始进行填充 119 | 2. 从插入的位置到end的距离小于了要插入的个数 120 | 1. 先构造出n - elems_after个大小的空间, 再从finish位置初始化n - elems_after为x 121 | 2. 从插入位置开始到原来的finish位置结束全部复制到新的结束位置后面 122 | 3. 从插入位置进行填充x 123 | 124 | - 上面分成两种情况就只是为了插入后的元素移动方便. 其实我自己考虑过为什么直接分配出n个空间, 然后从position+n处开始把原数据依次拷贝再将数据插入过去就行了, 没有必要分成两种情况, 可能这里面有什么我没有考虑到的因素吧. 125 | 126 | 2. 备用空间不足的时候执行 127 | 128 | 1. 重新申请一个当前两倍的空间或者当前大小+插入的空间, 选择两者最大的方案. 129 | 2. 进行分段复制到新的数组中, 从而实现插入 130 | 3. 将当前数组的元素进行析构, 最后释放空间 131 | 4. 修改3个迭代器 132 | 133 | ```c++ 134 | template 135 | void vector::insert(iterator position, size_type n, const T& x) 136 | { 137 | if (n != 0) 138 | { 139 | // ******** 1 *********** 140 | // 备用空间足够大 141 | if (size_type(end_of_storage - finish) >= n) 142 | { 143 | T x_copy = x; 144 | // 保存插入位置到end的距离 145 | const size_type elems_after = finish - position; 146 | iterator old_finish = finish; 147 | // ******* a ********** 148 | // 从插入的位置到数据结束的距离大于了要插入的个数n 149 | if (elems_after > n) 150 | { 151 | // 先构造出finish-n个大小的空间, 再移动finish - n个元素的数据 152 | uninitialized_copy(finish - n, finish, finish); 153 | finish += n; 154 | // 在将从插入位置后的n个元素移动 155 | copy_backward(position, old_finish - n, old_finish); 156 | // 元素从插入位置开始进行填充即可 157 | fill(position, position + n, x_copy); 158 | } 159 | // ********* b ********* 160 | // 从插入的位置到end的距离小于了要插入的个数 161 | else 162 | { 163 | // 先构造出n - elems_after个大小的空间, 再从finish位置初始化n - elems_after为x 164 | uninitialized_fill_n(finish, n - elems_after, x_copy); 165 | finish += n - elems_after; 166 | // 从插入位置开始到原来的finish位置结束全部复制到新的结束位置后面 167 | uninitialized_copy(position, old_finish, finish); 168 | finish += elems_after; 169 | // 从插入位置进行填充x 170 | fill(position, old_finish, x_copy); 171 | } 172 | } 173 | // ******* 2 *********** 174 | // 空间不足处理 175 | else 176 | { 177 | // 重新申请一个当前两倍的空间或者当前大小+插入的空间, 选择两者最大的方案. 178 | const size_type old_size = size(); 179 | const size_type len = old_size + max(old_size, n); 180 | iterator new_start = data_allocator::allocate(len); 181 | iterator new_finish = new_start; 182 | __STL_TRY 183 | { 184 | // 同样进行分段复制到新的数组中, 从而实现插入 185 | new_finish = uninitialized_copy(start, position, new_start); 186 | new_finish = uninitialized_fill_n(new_finish, n, x); 187 | new_finish = uninitialized_copy(position, finish, new_finish); 188 | } 189 | # ifdef __STL_USE_EXCEPTIONS 190 | catch(...) 191 | { 192 | destroy(new_start, new_finish); 193 | data_allocator::deallocate(new_start, len); 194 | throw; 195 | } 196 | # endif /* __STL_USE_EXCEPTIONS */ 197 | // 将当前数组的元素进行析构, 最后释放空间 198 | destroy(start, finish); 199 | deallocate(); 200 | // 修改3个迭代器 201 | start = new_start; 202 | finish = new_finish; 203 | end_of_storage = new_start + len; 204 | } 205 | } 206 | } 207 | ``` 208 | 209 | 210 | 211 | **insert(iterator position, const_iterator first, const_iterator last)** 传入3个迭代器, 进行范围插入. 212 | 213 | 整个流程与上面的操作基本一样, 所以这里也不详细分析了. 214 | 215 | ```c++ 216 | template 217 | void vector::insert(iterator position, const_iterator first, const_iterator last) { 218 | // 插入不为空 219 | if (first != last) { 220 | size_type n = 0; 221 | // 计算插入的长度, 并保存在n中 222 | distance(first, last, n); 223 | // 如果是剩余的空间大于插入的长度. 224 | if (size_type(end_of_storage - finish) >= n) { 225 | // 保存插入点到end的距离 226 | const size_type elems_after = finish - position; 227 | iterator old_finish = finish; 228 | // 同样比较n与插入点到end的距离, 下面的过程与上面描述的基本一致. 229 | // 从插入的位置到end的距离大于要插入的个数n 230 | if (elems_after > n) { 231 | uninitialized_copy(finish - n, finish, finish); 232 | finish += n; 233 | copy_backward(position, old_finish - n, old_finish); 234 | copy(first, last, position); 235 | } 236 | else { 237 | uninitialized_copy(first + elems_after, last, finish); 238 | finish += n - elems_after; 239 | uninitialized_copy(position, old_finish, finish); 240 | finish += elems_after; 241 | copy(first, first + elems_after, position); 242 | } 243 | } 244 | else { 245 | const size_type old_size = size(); 246 | const size_type len = old_size + max(old_size, n); 247 | iterator new_start = data_allocator::allocate(len); 248 | iterator new_finish = new_start; 249 | __STL_TRY { 250 | new_finish = uninitialized_copy(start, position, new_start); 251 | new_finish = uninitialized_copy(first, last, new_finish); 252 | new_finish = uninitialized_copy(position, finish, new_finish); 253 | } 254 | # ifdef __STL_USE_EXCEPTIONS 255 | catch(...) { 256 | destroy(new_start, new_finish); 257 | data_allocator::deallocate(new_start, len); 258 | throw; 259 | } 260 | # endif /* __STL_USE_EXCEPTIONS */ 261 | destroy(start, finish); 262 | deallocate(); 263 | start = new_start; 264 | finish = new_finish; 265 | end_of_storage = new_start + len; 266 | } 267 | } 268 | } 269 | ``` 270 | 271 | 272 | 273 | ## vector实例 274 | 275 | 我将vector的操作放在最后, 想这insert的实现有多种, 不一定都用过, 将insert分析完后再来实现印象. 276 | 277 | 这里我主要为了验证`vector`在插入时备用空间不足的情况下的例子, 同时用上了3种插入方法. 278 | 279 | ```c++ 280 | class T 281 | { 282 | public: 283 | T() { cout << "construct" << endl;} 284 | ~T() { cout << "destruct" << endl; } 285 | }; 286 | 287 | int main() 288 | { 289 | vector v1; 290 | cout << "array size = " << v1.capacity() << endl; 291 | cout << endl; 292 | 293 | v1.insert(v1.begin(), T()); 294 | cout << "array size = " << v1.capacity() << endl; 295 | cout << endl; 296 | 297 | v1.insert(v1.begin(), v1.begin(), v1.end()); 298 | cout << "array size = " << v1.capacity() << endl; 299 | cout << endl; 300 | 301 | v1.insert(v1.begin(), v1.size(), T()); 302 | cout << "array size = " << v1.capacity() << endl; 303 | cout << endl; 304 | 305 | v1.insert(v1.begin(), v1.size(), T()); 306 | cout << "array size = " << v1.capacity() << endl; 307 | cout << endl; 308 | 309 | exit(0); 310 | } 311 | ``` 312 | 313 | 输出结果: 314 | 315 | ```c++ 316 | array size = 0 317 | 318 | construct 319 | destruct 320 | array size = 1 321 | 322 | destruct 323 | array size = 2 324 | 325 | construct 326 | destruct 327 | destruct 328 | destruct 329 | array size = 4 330 | 331 | construct 332 | destruct 333 | destruct 334 | destruct 335 | destruct 336 | destruct 337 | array size = 8 338 | ``` 339 | 340 | 明显可以看出每次插入是空间不足都会重新寻找新的空间然后析构释放掉原始空间. **所以`vector`插入操作并不是非常的高效, 效率很低, 代价高.** 341 | 342 | 343 | 344 | ## 总结 345 | 346 | 本节分析了`insert`和删除的操作, 可能都清楚了为什么建议一个经常调用随机插入和删除操作就尽量不要用`vector`, 因为开销有时真的很大, 如果经常插入不如用`list`, 因为`list`对随机插入和删除的开销比`vector`小的多, `list`的用法我们下节再来分析, 347 | 348 | `vector`适合用来做随机访问很快, 也支持适合频率较低的插入和删除操作, 在每次插入和删除后迭代器都会失效, 因为不能保证操作之后迭代器的位置没有改变. -------------------------------------------------------------------------------- /14 list 上.md: -------------------------------------------------------------------------------- 1 | # list 2 | 3 | ## 前言 4 | 5 | 前几节我们分析了`vector`的实现, `vector`的缺点也很明显, 在频率较高的插入和删除时效率就太低了, 本节我们就来分析在频率较高的插入和删除很也很好的效率的`list`. 6 | 7 | `list`是用链表进行实现的, 而链表对删除, 插入的时间复杂度为O(1), 效率相当高, 但是随机访问的时间复杂度为O(n). `list`将具体实现分成几个部分, 通过嵌套的方式进行调用, 所以list实现也很灵活. 而且**`list`在插入和删除操作后迭代器并不会失效.** 8 | 9 | 本节只分析关于`list`的结构, 构造和析构函数的实现. 10 | 11 | 12 | 13 | ## list基本结构框架 14 | 15 | `list`将基本的框架分成`__list_node`(实现节点), `__list_iterator`(实现迭代器)两部分方便随时调用. 下面我们就先慢慢分析. 16 | 17 | 18 | 19 | ### __list_node链表结构 20 | 21 | `__list_node`用来实现节点, 数据结构中就储存前后指针和属性. 22 | 23 | ```c++ 24 | template struct __list_node 25 | { 26 | // 前后指针 27 | typedef void* void_pointer; 28 | void_pointer next; 29 | void_pointer prev; 30 | // 属性 31 | T data; 32 | }; 33 | ``` 34 | 35 | 36 | 37 | ### __list_iterator结构 38 | 39 | **基本类型** 40 | 41 | ```c++ 42 | template struct __list_iterator 43 | { 44 | typedef __list_iterator iterator; // 迭代器 45 | typedef __list_iterator const_iterator; 46 | typedef __list_iterator self; 47 | 48 | // 迭代器是bidirectional_iterator_tag类型 49 | typedef bidirectional_iterator_tag iterator_category; 50 | typedef T value_type; 51 | typedef Ptr pointer; 52 | typedef Ref reference; 53 | typedef size_t size_type; 54 | typedef ptrdiff_t difference_type; 55 | ... 56 | }; 57 | ``` 58 | 59 | **构造函数** 60 | 61 | ```c++ 62 | template struct __list_iterator 63 | { 64 | ... 65 | // 定义节点指针 66 | typedef __list_node* link_type; 67 | link_type node; 68 | // 构造函数 69 | __list_iterator(link_type x) : node(x) {} 70 | __list_iterator() {} 71 | __list_iterator(const iterator& x) : node(x.node) {} 72 | ... 73 | }; 74 | ``` 75 | 76 | **重载** 77 | 78 | ```c++ 79 | template struct __list_iterator 80 | { 81 | ... 82 | // 重载 83 | bool operator==(const self& x) const { return node == x.node; } 84 | bool operator!=(const self& x) const { return node != x.node; } 85 | // 对*和->操作符进行重载 86 | reference operator*() const { return (*node).data; } 87 | #ifndef __SGI_STL_NO_ARROW_OPERATOR 88 | pointer operator->() const { return &(operator*()); } 89 | #endif /* __SGI_STL_NO_ARROW_OPERATOR */ 90 | 91 | // ++和--是直接操作的指针指向next还是prev, 因为list是一个双向链表 92 | self& operator++() 93 | { 94 | node = (link_type)((*node).next); 95 | return *this; 96 | } 97 | self operator++(int) 98 | { 99 | self tmp = *this; 100 | ++*this; 101 | return tmp; 102 | } 103 | self& operator--() 104 | { 105 | node = (link_type)((*node).prev); 106 | return *this; 107 | } 108 | self operator--(int) 109 | { 110 | self tmp = *this; 111 | --*this; 112 | return tmp; 113 | } 114 | }; 115 | ``` 116 | 117 | 118 | 119 | ## list结构 120 | 121 | `list`自己定义了嵌套类型满足`traits`编程. `list`迭代器是`bidirectional_iterator_tag`类型, 并不是一个普通指针. 122 | 123 | **list基本类型定义** 124 | 125 | **`list`在定义node节点时, 定义的不是一个指针, 这里要注意.** 126 | 127 | ```c++ 128 | template 129 | class list 130 | { 131 | protected: 132 | typedef void* void_pointer; 133 | typedef __list_node list_node; // 节点 134 | typedef simple_alloc list_node_allocator; // 空间配置器 135 | public: 136 | // 定义嵌套类型 137 | typedef T value_type; 138 | typedef value_type* pointer; 139 | typedef const value_type* const_pointer; 140 | typedef value_type& reference; 141 | typedef const value_type& const_reference; 142 | typedef list_node* link_type; 143 | typedef size_t size_type; 144 | typedef ptrdiff_t difference_type; 145 | 146 | protected: 147 | // 定义一个节点, 这里节点并不是一个指针. 148 | link_type node; 149 | 150 | public: 151 | // 定义迭代器 152 | typedef __list_iterator iterator; 153 | typedef __list_iterator const_iterator; 154 | ... 155 | }; 156 | ``` 157 | 158 | 159 | 160 | **list构造和析构函数实现** 161 | 162 | 构造函数前期准备 163 | 164 | 1. 分配空间`get_node` 165 | 2. 释放空间`put_node` 166 | 3. 分配并构造`create_node` 167 | 4. 析构并释放空间`destroy_node` 168 | 5. 对节点进行初始化`empty_initialize` 169 | 170 | ```c++ 171 | template 172 | class list 173 | { 174 | ... 175 | protected: 176 | // 分配一个元素大小的空间, 返回分配的地址 177 | link_type get_node() { return list_node_allocator::allocate(); } 178 | // 释放一个元素大小的内存 179 | void put_node(link_type p) { list_node_allocator::deallocate(p); } 180 | // 分配一个元素大小的空间并调用构造初始化内存 181 | link_type create_node(const T& x) 182 | { 183 | link_type p = get_node(); 184 | __STL_TRY { 185 | construct(&p->data, x); 186 | } 187 | __STL_UNWIND(put_node(p)); 188 | return p; 189 | } 190 | // 调用析构并释放一个元素大小的空间 191 | void destroy_node(link_type p) { 192 | destroy(&p->data); 193 | put_node(p); 194 | } 195 | // 对节点初始化 196 | void empty_initialize() 197 | { 198 | node = get_node(); 199 | node->next = node; 200 | node->prev = node; 201 | } 202 | ... 203 | }; 204 | ``` 205 | 206 | **构造函数** 207 | 208 | 1. 多个重载, 以实现直接构造n个节点并初始化一个值, 支持传入迭代器进行范围初始化, 也支持接受一个`list`参数, 同样进行范围初始化. 209 | 2. **每个构造函数都会创造一个空的node节点, 为了保证我们在执行任何操作都不会修改迭代器.** 210 | 211 | ```c++ 212 | template 213 | class list 214 | { 215 | ... 216 | protected: 217 | // 构造函数 218 | list() { empty_initialize(); } // 默认构造函数, 分配一个空的node节点 219 | // 都调用同一个函数进行初始化 220 | list(size_type n, const T& value) { fill_initialize(n, value); } 221 | list(int n, const T& value) { fill_initialize(n, value); } 222 | list(long n, const T& value) { fill_initialize(n, value); } 223 | // 分配n个节点 224 | explicit list(size_type n) { fill_initialize(n, T()); } 225 | 226 | #ifdef __STL_MEMBER_TEMPLATES 227 | // 接受两个迭代器进行范围的初始化 228 | template 229 | list(InputIterator first, InputIterator last) { 230 | range_initialize(first, last); 231 | } 232 | #else /* __STL_MEMBER_TEMPLATES */ 233 | // 接受两个迭代器进行范围的初始化 234 | list(const T* first, const T* last) { range_initialize(first, last); } 235 | list(const_iterator first, const_iterator last) { 236 | range_initialize(first, last); 237 | } 238 | #endif /* __STL_MEMBER_TEMPLATES */ 239 | // 接受一个list参数, 进行拷贝 240 | list(const list& x) { 241 | range_initialize(x.begin(), x.end()); 242 | } 243 | list& operator=(const list& x); 244 | ... 245 | }; 246 | ``` 247 | 248 | 构造函数内大都调用这个函数, 可以看出来`list`在初始化的时候都会**构造一个空的`node`节点**, 然后对元素进行`insert`插入操作. 249 | 250 | ```c++ 251 | void fill_initialize(size_type n, const T& value) { 252 | empty_initialize(); 253 | __STL_TRY { 254 | insert(begin(), n, value); 255 | } 256 | __STL_UNWIND(clear(); put_node(node)); 257 | } 258 | ``` 259 | 260 | **析构函数** , 释放所有的节点空间. 包括最初的空节点. 261 | 262 | ```c++ 263 | ~list() { 264 | // 删除初空节点以外的所有节点 265 | clear(); 266 | // 删除空节点 267 | put_node(node); 268 | } 269 | ``` 270 | 271 | 272 | 273 | ## 基本属性获取 274 | 275 | 要注意一点`list`中的迭代器一般不会被修改, 因为`node`节点始终指向的一个空节点同时`list`是一个循环的链表, 空节点正好在头和尾的中间, 所以`node.next`就是指向头的指针, `node.prev`就是指向结束的指针, `end`返回的是最后一个数据的后一个地址也就是`node`. 清楚这些后就容易看懂下面怎么获取属性了. 276 | 277 | ```c++ 278 | template 279 | class list 280 | { 281 | ... 282 | public: 283 | iterator begin() { return (link_type)((*node).next); } // 返回指向头的指针 284 | const_iterator begin() const { return (link_type)((*node).next); } 285 | iterator end() { return node; } // 返回最后一个元素的后一个的地址 286 | const_iterator end() const { return node; } 287 | 288 | // 这里是为旋转做准备, rbegin返回最后一个地址, rend返回第一个地址. 我们放在配接器里面分析 289 | reverse_iterator rbegin() { return reverse_iterator(end()); } 290 | const_reverse_iterator rbegin() const { 291 | return const_reverse_iterator(end()); 292 | } 293 | reverse_iterator rend() { return reverse_iterator(begin()); } 294 | const_reverse_iterator rend() const { 295 | return const_reverse_iterator(begin()); 296 | } 297 | 298 | // 判断是否为空链表, 这是判断只有一个空node来表示链表为空. 299 | bool empty() const { return node->next == node; } 300 | // 因为这个链表, 地址并不连续, 所以要自己迭代计算链表的长度. 301 | size_type size() const { 302 | size_type result = 0; 303 | distance(begin(), end(), result); 304 | return result; 305 | } 306 | size_type max_size() const { return size_type(-1); } 307 | // 返回第一个元素的值 308 | reference front() { return *begin(); } 309 | const_reference front() const { return *begin(); } 310 | // 返回最后一个元素的值 311 | reference back() { return *(--end()); } 312 | const_reference back() const { return *(--end()); } 313 | 314 | // 交换 315 | void swap(list& x) { __STD::swap(node, x.node); } 316 | ... 317 | }; 318 | template 319 | inline void swap(list& x, list& y) 320 | { 321 | x.swap(y); 322 | } 323 | ``` 324 | 325 | 326 | 327 | ## 总结 328 | 329 | 本节仅仅对list基本类型, 构造, 析构和怎么获取属性做了分析, 将`list`拆开一步步分析, 下节继续分析`list`, 主要探讨其插入,删除操作. 330 | 331 | -------------------------------------------------------------------------------- /15 list 中.md: -------------------------------------------------------------------------------- 1 | # list 2 | 3 | ## 前言 4 | 5 | 上节分析了`list`的类型, 构造析构的实现, 本节我们着重探讨`list`的push, pop, 插入和删除等基本操作. 6 | 7 | 8 | 9 | ## list实际操作 10 | 11 | 这里写了一些关于本节大都会用到的操作, 执行完后我们就来分析函数的具体实现. 12 | 13 | ```c++ 14 | int main() 15 | { 16 | list List; 17 | List.push_back(1); 18 | List.push_front(0); 19 | List.push_back(2); 20 | List.push_front(3); 21 | List.insert(++List.begin(), 5); // list没有重载 + 运算符, 所以不能直接操作迭代器 22 | 23 | list List1(List); 24 | list List2 = List1; 25 | cout << "list size = " << List2.size() << endl; 26 | if(!List2.empty()) 27 | for (auto i : List2) 28 | { 29 | cout << i << " "; 30 | } 31 | cout << endl; 32 | 33 | exit(0); 34 | } 35 | ``` 36 | 37 | 输出 38 | 39 | ```c++ 40 | list size = 5 41 | 3 5 0 1 2 42 | ``` 43 | 44 | 45 | 46 | ## list操作 47 | 48 | 49 | 50 | ### push和pop操作 51 | 52 | 因为`list`是一个循环的双链表, 所以push和pop就必须实现是在头插入, 删除还是在尾插入和删除. push操作都调用`insert`函数, pop操作都调用`erase`函数. 53 | 54 | ```c++ 55 | template 56 | class list 57 | { 58 | ... 59 | // 直接在头部或尾部插入 60 | void push_front(const T& x) { insert(begin(), x); } 61 | void push_back(const T& x) { insert(end(), x); } 62 | // 直接在头部或尾部删除 63 | void pop_front() { erase(begin()); } 64 | void pop_back() { 65 | iterator tmp = end(); 66 | erase(--tmp); 67 | } 68 | ... 69 | }; 70 | ``` 71 | 72 | 73 | 74 | ### 删除操作 75 | 76 | 删除元素的操作大都是由`erase`函数来实现的, 其他的所有函数都是直接或间接调用`erase`. `list`是链表, 所以链表怎么实现删除, list就在怎么操作. 77 | 78 | ```c++ 79 | template 80 | class list 81 | { 82 | ... 83 | iterator erase(iterator first, iterator last); 84 | void clear(); 85 | // 参数是一个迭代器 86 | // 修改该元素的前后指针指向再单独释放节点就行了 87 | iterator erase(iterator position) { 88 | link_type next_node = link_type(position.node->next); 89 | link_type prev_node = link_type(position.node->prev); 90 | prev_node->next = next_node; 91 | next_node->prev = prev_node; 92 | destroy_node(position.node); 93 | return iterator(next_node); 94 | } 95 | ... 96 | }; 97 | // erase的重载, 删除两个迭代器之间的元素 98 | template 99 | list::iterator list::erase(iterator first, iterator last) 100 | { 101 | // 就是一次次调用erase进行删除 102 | while (first != last) 103 | erase(first++); 104 | return last; 105 | } 106 | // remove调用erase链表清除 107 | template 108 | void list::remove(const T& value) { 109 | iterator first = begin(); 110 | iterator last = end(); 111 | while (first != last) { 112 | iterator next = first; 113 | ++next; 114 | if (*first == value) erase(first); 115 | first = next; 116 | } 117 | } 118 | ``` 119 | 120 | `clear`函数是删除除空节点以外的所有节点, 即只留下了最初创建的空节点. 121 | 122 | ```c++ 123 | // 删除除空节点以外的所有节点 124 | template 125 | void list::clear() 126 | { 127 | link_type cur = (link_type) node->next; 128 | // 除空节点都删除 129 | while (cur != node) { 130 | link_type tmp = cur; 131 | cur = (link_type) cur->next; 132 | destroy_node(tmp); 133 | } 134 | node->next = node; 135 | node->prev = node; 136 | } 137 | ``` 138 | 139 | 140 | 141 | ### 重载 142 | 143 | `list`也提供了基本操作的重载, 所以我们使用`list`也很方便. 144 | 145 | *相等比较* 146 | 147 | ```c++ 148 | // 判断两个list相等 149 | template 150 | inline bool operator==(const list& x, const list& y) 151 | { 152 | typedef typename list::link_type link_type; 153 | link_type e1 = x.node; 154 | link_type e2 = y.node; 155 | link_type n1 = (link_type) e1->next; 156 | link_type n2 = (link_type) e2->next; 157 | // 将两个链表执行一一的对比来分析是否相等. 158 | // 这里不把元素个数进行一次比较, 主要获取个数时也要遍历整个数组, 所以就不将个数纳入比较 159 | for ( ; n1 != e1 && n2 != e2 ; n1 = (link_type) n1->next, n2 = (link_type) n2->next) 160 | if (n1->data != n2->data) 161 | return false; 162 | return n1 == e1 && n2 == e2; 163 | } 164 | ``` 165 | 166 | *小于比较* 167 | 168 | ```c++ 169 | template 170 | inline bool operator<(const list& x, const list& y) { 171 | return lexicographical_compare(x.begin(), x.end(), y.begin(), y.end()); 172 | } 173 | ``` 174 | 175 | *赋值操作* 176 | 177 | 需要考虑两个链表的实际大小不一样时的操作. 178 | 179 | 1. 原链表大 : 复制完后要删除掉原链表多余的元素 180 | 2. 原链表小 : 复制完后要还要将x链表的剩余元素以插入的方式插入到原链表中 181 | 182 | ```c++ 183 | template 184 | list& list::operator=(const list& x) { 185 | if (this != &x) { 186 | iterator first1 = begin(); 187 | iterator last1 = end(); 188 | const_iterator first2 = x.begin(); 189 | const_iterator last2 = x.end(); 190 | // 直到两个链表有一个空间用尽 191 | while (first1 != last1 && first2 != last2) 192 | *first1++ = *first2++; 193 | // 原链表大, 复制完后要删除掉原链表多余的元素 194 | if (first2 == last2) 195 | erase(first1, last1); 196 | // 原链表小, 复制完后要还要将x链表的剩余元素以插入的方式插入到原链表中 197 | else 198 | insert(last1, first2, last2); 199 | } 200 | return *this; 201 | } 202 | ``` 203 | 204 | 205 | 206 | ### resize操作 207 | 208 | `resize`重新修改`list`的大小 209 | 210 | ```c++ 211 | template 212 | class list 213 | { 214 | ... 215 | resize(size_type new_size, const T& x); 216 | void resize(size_type new_size) { resize(new_size, T()); } 217 | ... 218 | }; 219 | template 220 | void list::resize(size_type new_size, const T& x) 221 | { 222 | iterator i = begin(); 223 | size_type len = 0; 224 | for ( ; i != end() && len < new_size; ++i, ++len) 225 | ; 226 | // 如果链表长度大于new_size的大小, 那就删除后面多余的节点 227 | if (len == new_size) 228 | erase(i, end()); 229 | // i == end(), 扩大链表的节点 230 | else 231 | insert(end(), new_size - len, x); 232 | } 233 | ``` 234 | 235 | 236 | 237 | ### unique操作 238 | 239 | **`unique`函数是将数值相同且连续的元素删除, 只保留一个副本.** 记住, `unique`*并不是删除所有的相同元素, 而是连续的相同元素, 如果要删除所有相同元素就要对list做一个排序在进行unique操作.* 240 | 241 | 一般unique同sort一起用的. `sort`函数准备放在下一节来分析. 242 | 243 | ```c++ 244 | template template 245 | void list::unique(BinaryPredicate binary_pred) { 246 | iterator first = begin(); 247 | iterator last = end(); 248 | if (first == last) return; 249 | iterator next = first; 250 | // 删除连续相同的元素, 留一个副本 251 | while (++next != last) { 252 | if (binary_pred(*first, *next)) 253 | erase(next); 254 | else 255 | first = next; 256 | next = first; 257 | } 258 | } 259 | ``` 260 | 261 | 262 | 263 | ### insert操作 264 | 265 | `insert`函数有很多的重载函数, 满足足够用户的各种插入方法了. 但是最核心的还是`iterator insert(iterator position, const T& x)`, 每一个重载函数都是直接或间接的调用该函数. 266 | 267 | `insert`是将元素插入到指定地址的前一个位置. 268 | 269 | ```c++ 270 | template 271 | class list 272 | { 273 | ... 274 | public: 275 | // 最基本的insert操作, 之插入一个元素 276 | iterator insert(iterator position, const T& x) 277 | { 278 | // 将元素插入指定位置的前一个地址 279 | link_type tmp = create_node(x); 280 | tmp->next = position.node; 281 | tmp->prev = position.node->prev; 282 | (link_type(position.node->prev))->next = tmp; 283 | position.node->prev = tmp; 284 | return tmp; 285 | } 286 | 287 | // 以下重载函数都是调用iterator insert(iterator position, const T& x)函数 288 | iterator insert(iterator position) { return insert(position, T()); } 289 | #ifdef __STL_MEMBER_TEMPLATES 290 | template 291 | void insert(iterator position, InputIterator first, InputIterator last); 292 | #else /* __STL_MEMBER_TEMPLATES */ 293 | void insert(iterator position, const T* first, const T* last); 294 | void insert(iterator position, 295 | const_iterator first, const_iterator last); 296 | #endif /* __STL_MEMBER_TEMPLATES */ 297 | void insert(iterator pos, size_type n, const T& x); 298 | void insert(iterator pos, int n, const T& x) { 299 | insert(pos, (size_type)n, x); 300 | } 301 | void insert(iterator pos, long n, const T& x) { 302 | insert(pos, (size_type)n, x); 303 | } 304 | void resize(size_type new_size, const T& x); 305 | ... 306 | }; 307 | 308 | #ifdef __STL_MEMBER_TEMPLATES 309 | template template 310 | void list::insert(iterator position, InputIterator first, InputIterator last) 311 | { 312 | for ( ; first != last; ++first) 313 | insert(position, *first); 314 | } 315 | #else /* __STL_MEMBER_TEMPLATES */ 316 | template 317 | void list::insert(iterator position, const T* first, const T* last) { 318 | for ( ; first != last; ++first) 319 | insert(position, *first); 320 | } 321 | template 322 | void list::insert(iterator position, 323 | const_iterator first, const_iterator last) { 324 | for ( ; first != last; ++first) 325 | insert(position, *first); 326 | } 327 | #endif /* __STL_MEMBER_TEMPLATES */ 328 | template 329 | void list::insert(iterator position, size_type n, const T& x) { 330 | for ( ; n > 0; --n) 331 | insert(position, x); 332 | } 333 | ``` 334 | 335 | 336 | 337 | 338 | 339 | ## 总结 340 | 341 | 本节分析了`list`的插入, 删除, 重载等操作, 这些都是链表的基本操作, 相信大家看的时候应该也没有什么问题, 我将最难的部分--`sort`等操作放在下一节来分析. 342 | 343 | 这里还是提醒一下: 344 | 345 | 1. **节点实际是以`node`空节点开始的** 346 | 2. **插入操作是将元素插入到指定位置的前一个地址进行插入的.** -------------------------------------------------------------------------------- /16 list 下.md: -------------------------------------------------------------------------------- 1 | # list 2 | 3 | ## 前言 4 | 5 | 前两节对`list`的push, pop, insert等操作做了分析, 本节准备探讨`list`怎么实现`sort`功能. 6 | 7 | `list`是一个循环双向链表, 不是一个连续地址空间, 所以`sort`功能需要特殊的算法单独实现, 而不能用算法中的sort. 当然还可以将`list`的元素插入到vector中最后在将vector排序好的数据拷贝回来, 不过这种做法很费时, 费效率. 8 | 9 | 10 | 11 | ## list操作实现 12 | 13 | 在分析`sort`之前先来分析`transfer`, `reverse`, `merge`这几个会被调用的函数. 14 | 15 | 16 | 17 | ### transfer函数 18 | 19 | **`transfer`函数功能是将一段链表插入到我们指定的位置之前**. 该函数一定要理解, 后面分析的所有函数都是该基础上进行修改的. 20 | 21 | `transfer`函数接受3个迭代器. 第一个迭代器表示链表要插入的位置, `first`到`last`最闭右开区间插入到`position`之前. 22 | 23 | 24 | 25 | 从`if`下面开始分析(*这里我将源码的执行的先后顺序进行的部分调整, 下面我分析的都是调整顺序过后的代码. 当然我也会把源码顺序写下来, 以便参考*) 26 | 27 | - **为了避免待会解释起来太绕口, 这里先统一一下部分名字** 28 | 29 | 1. `last`的前一个节点叫`last_but_one` 30 | 2. `first`的前一个节点叫`zero` 31 | 32 | - 好, 现在我们开始分析`transfer`的每一步(*最好在分析的时候在纸上画出两个链表一步步来画*) 33 | 34 | 1. 第一行. `last_but_one`的`next`指向插入的`position`节点 35 | 2. 第二行. `position`的`next`指向`last_but_one` 36 | 3. 第三行. 临时变量`tmp`保存`position`的前一个节点 37 | 4. 第四行. `first`的`prev`指向`tmp` 38 | 5. 第五行. `position`的前一个节点的`next`指向`first`节点 39 | 6. 第六行. `zero`的`next`指向`last`节点 40 | 7. 第七行. `last`的`prev`指向`zero` 41 | 42 | ```c++ 43 | template 44 | class list 45 | { 46 | ... 47 | protected: 48 | void transfer(iterator position, iterator first, iterator last) 49 | { 50 | if (position != last) 51 | { 52 | (*(link_type((*last.node).prev))).next = position.node; 53 | (*position.node).prev = (*last.node).prev; 54 | link_type tmp = link_type((*position.node).prev); 55 | (*first.node).prev = tmp; 56 | (*(link_type((*position.node).prev))).next = first.node; 57 | (*(link_type((*first.node).prev))).next = last.node; 58 | (*last.node).prev = (*first.node).prev; 59 | } 60 | } 61 | /* 62 | void transfer(iterator position, iterator first, iterator last) 63 | { 64 | if (position != last) 65 | { 66 | (*(link_type((*last.node).prev))).next = position.node; 67 | (*(link_type((*first.node).prev))).next = last.node; 68 | (*(link_type((*position.node).prev))).next = first.node; 69 | link_type tmp = link_type((*position.node).prev); 70 | (*position.node).prev = (*last.node).prev; 71 | (*last.node).prev = (*first.node).prev; 72 | (*first.node).prev = tmp; 73 | } 74 | } 75 | */ 76 | ... 77 | }; 78 | ``` 79 | 80 | **splice** 将两个链表进行合并. 81 | 82 | ```c++ 83 | template 84 | class list 85 | { 86 | ... 87 | public: 88 | void splice(iterator position, list& x) { 89 | if (!x.empty()) 90 | transfer(position, x.begin(), x.end()); 91 | } 92 | void splice(iterator position, list&, iterator i) { 93 | iterator j = i; 94 | ++j; 95 | if (position == i || position == j) return; 96 | transfer(position, i, j); 97 | } 98 | void splice(iterator position, list&, iterator first, iterator last) { 99 | if (first != last) 100 | transfer(position, first, last); 101 | } 102 | ... 103 | }; 104 | ``` 105 | 106 | 107 | 108 | ### merge函数 109 | 110 | `merge`函数接受一个`list`参数. 111 | 112 | **`merge`函数是将传入的`list`链表x与原链表按从小到大合并到原链表中(前提是两个链表都是已经从小到大排序了)**. 这里`merge`的核心就是`transfer`函数. 113 | 114 | ```c++ 115 | template 116 | void list::merge(list& x) { 117 | iterator first1 = begin(); 118 | iterator last1 = end(); 119 | iterator first2 = x.begin(); 120 | iterator last2 = x.end(); 121 | while (first1 != last1 && first2 != last2) 122 | if (*first2 < *first1) { 123 | iterator next = first2; 124 | // 将first2到first+1的左闭右开区间插入到first1的前面 125 | // 这就是将first2合并到first1链表中 126 | transfer(first1, first2, ++next); 127 | first2 = next; 128 | } 129 | else 130 | ++first1; 131 | // 如果链表x还有元素则全部插入到first1链表的尾部 132 | if (first2 != last2) transfer(last1, first2, last2); 133 | } 134 | ``` 135 | 136 | 137 | 138 | ### reverse函数 139 | 140 | **`reverse`函数是实现将链表翻转的功能.** 主要是`list`的迭代器基本不会改变的特点, 将每一个元素一个个插入到`begin`之前. 这里注意迭代器不会变, 但是`begin`会改变, 它始终指向第一个元素的地址. 141 | 142 | ```c++ 143 | template 144 | void list::reverse() 145 | { 146 | if (node->next == node || link_type(node->next)->next == node) 147 | return; 148 | iterator first = begin(); 149 | ++first; 150 | while (first != end()) { 151 | iterator old = first; 152 | ++first; 153 | // 将元素插入到begin()之前 154 | transfer(begin(), old, first); 155 | } 156 | } 157 | ``` 158 | 159 | 160 | 161 | ### sort 162 | 163 | `list`实现`sort` 功能本身就不容易, 当我分析了之后就对其表示佩服. 严格的说`list`排序的时间复杂度应为`nlog(n)`, 其实现用了归并排序的思想, 将所有元素分成n分, 总共2^n个元素. 164 | 165 | 这个sort的分析 : 166 | 167 | - 这里将每个重要的参数列出来解释其含义 168 | 169 | 1. `fill` : 当前可以处理的元素个数为2^fill个 170 | 171 | 2. `counter[fill]` : 可以容纳2^(fill+1)个元素 172 | 3. `carry` : 一个临时中转站, 每次将一元素插入到counter[i]链表中. 173 | 174 | 175 | 在处理的元素个数不足2^fill个时,在`counter[i](0 188 | void list::sort() 189 | { 190 | //如果元素个数小于等于1,直接返回 191 | if(node->next==node||node->next->next==node) 192 | return ; 193 | list carry; //中转站 194 | list counter[64]; 195 | int fill=0; 196 | while(!empty()) 197 | { 198 | carry.splice(carry.begin(),*this,begin()); //每次取出一个元素 199 | int i=0; 200 | while(i 18 | class __malloc_alloc_template 19 | { 20 | // 这里private里面的函数都是在内存不足的时候进行调用的 21 | private: 22 | static void *oom_malloc(size_t); // 分配不足 23 | static void *oom_realloc(void *, size_t); // 重新分配不足 24 | #ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG 25 | static void (* __malloc_alloc_oom_handler)(); // 内存不足设置的处理例程, 默认设置的是0, 表示没有设置处理例程, 这个处理例程是由用户手动设置的 26 | #endif 27 | public: 28 | }; 29 | ``` 30 | 31 | 32 | 33 | 唯一比较麻烦的就是`set_malloc_handler` 它就是接受一个函数指针, 用来保存用户自定义的处理函数, 如果用户没有设置的话, 默认就设置为0. 因为处理函数会跟后面的内存不足有关系. 34 | 35 | ```c++ 36 | // 这里是模仿c++的set_new_handler. 是由用户自己定义的处理函数, 没有设置默认为0 37 | static void (* set_malloc_handler(void (*f)()))() 38 | { 39 | void (* old)() = __malloc_alloc_oom_handler; 40 | __malloc_alloc_oom_handler = f; 41 | return(old); 42 | } 43 | ``` 44 | 45 | 默认将处理例程设置为0, 只有用户自己设置. 46 | 47 | ```c++ 48 | template 49 | void (* __malloc_alloc_template::__malloc_alloc_oom_handler)() = 0; 50 | ``` 51 | 52 | 53 | 54 | #### allocate 55 | 56 | `allocate` : 很明显, 这里直接调用`malloc`分配内存, 当内存不足的时候, 程序继续调用`oom_malloc`来选择抛出异常还是一直申请内存, 直到申请内存成功. 57 | 58 | ```c++ 59 | // 在分配和再次分配中, 都会检查内存不足, 在不足的时候直接调用private中相应的函数 60 | static void * allocate(size_t n) 61 | { 62 | void *result = malloc(n); 63 | if (0 == result) result = oom_malloc(n); 64 | return result; 65 | } 66 | ``` 67 | 68 | `oom_malloc`函数功能 : 除非用户自定义了处理例程, 否则当内存不足的时候直接输出内存不足的提示然后直接调用exit(1); 69 | 用户定义了处理程序, 函数会一直进行内存申请, 直到申请到内存为止 70 | 71 | ```c++ 72 | template 73 | void * __malloc_alloc_template::oom_malloc(size_t n) 74 | { 75 | void (* my_malloc_handler)(); 76 | void *result; 77 | // 用户自定义处理例程, 就一直申请内存, 否则抛出异常 78 | for (;;) 79 | { 80 | my_malloc_handler = __malloc_alloc_oom_handler; 81 | if (0 == my_malloc_handler) { __THROW_BAD_ALLOC; } 82 | (*my_malloc_handler)(); 83 | result = malloc(n); 84 | if (result) return(result); 85 | } 86 | } 87 | ``` 88 | 89 | 90 | 91 | #### deallocate 92 | 93 | 一级配置器直接调用free释放内存 94 | 95 | ```c++ 96 | static void deallocate(void *p, size_t /* n */) 97 | { 98 | free(p); 99 | } 100 | ``` 101 | 102 | 103 | 104 | #### reallocate 105 | 106 | 下面的函数都是很简单的或是重复的功能, 就一笔带过. 107 | 108 | 这里reallocate和oom_realloc和上面`allocate`一样的, 这里就不做过多的解释了. 109 | 110 | ```c++ 111 | static void * reallocate(void *p, size_t /* old_sz */, size_t new_sz) 112 | { 113 | void * result = realloc(p, new_sz); 114 | if (0 == result) result = oom_realloc(p, new_sz); 115 | return result; 116 | } 117 | ``` 118 | 119 | ```c++ 120 | template 121 | void * __malloc_alloc_template::oom_realloc(void *p, size_t n) 122 | { 123 | void (* my_malloc_handler)(); 124 | void *result; 125 | 126 | for (;;) { 127 | my_malloc_handler = __malloc_alloc_oom_handler; 128 | if (0 == my_malloc_handler) { __THROW_BAD_ALLOC; } 129 | (*my_malloc_handler)(); 130 | result = realloc(p, n); 131 | if (result) return(result); 132 | } 133 | } 134 | ``` 135 | 136 | 137 | 138 | --- 139 | 140 | 程序默认定义`mallo_alloc`函数, 并且设置统一的调用接口, 默认的的接口为第二级配置器 141 | 142 | ```c++ 143 | // 默认将malloc_alloc设为0; 144 | typedef __malloc_alloc_template<0> malloc_alloc; 145 | ``` 146 | 147 | 148 | 149 | #### 统一的接口 150 | 151 | 定义符合STL规格的配置器接口, 不管是一级配置器还是二级配置器都是使用这个接口进行分配的 152 | 153 | ```c++ 154 | // 定义符合STL规格的配置器接口, 不管是一级配置器还是二级配置器都是使用这个接口进行分配的 155 | template 156 | class simple_alloc { 157 | public: 158 | static T *allocate(size_t n) 159 | { return 0 == n? 0 : (T*) Alloc::allocate(n * sizeof (T)); } 160 | static T *allocate(void) 161 | { return (T*) Alloc::allocate(sizeof (T)); } 162 | static void deallocate(T *p, size_t n) 163 | { if (0 != n) Alloc::deallocate(p, n * sizeof (T)); } 164 | static void deallocate(T *p) 165 | { Alloc::deallocate(p, sizeof (T)); } 166 | }; 167 | ``` 168 | 169 | 170 | 171 | ### 总结 172 | 173 | 本节对STL的第一级配置器做了分析, STL对malloc和free用函数重新进行了封装, 同时一级还是二级都做了统一的接口. 接下来我们继续分析第二级配置器. -------------------------------------------------------------------------------- /20 stack.md: -------------------------------------------------------------------------------- 1 | # stack 2 | 3 | ### 前言 4 | 5 | 上面几节我们分析了`deque`是一个双向开口, 并且每部分数据也是存放在连续空间中, 而`stack`是栈, 只允许在尾进行操作, 而且`stack`的功能`deque`都已经实现了, 只需要对`deque`的功能进行部分封装就行了, 也就提取出pop_back, push_back就行了. 6 | 7 | `stack`严格他并不是容器, 它是一底部容器完成其所有的工作, 它只修改了容器的接口, 准确是叫**配接器**. 8 | 9 | 10 | 11 | ### stack源码 12 | 13 | `stack`也满足`straits`编程规范. 14 | 15 | ```c++ 16 | #ifndef __STL_LIMITED_DEFAULT_TEMPLATES 17 | template > 18 | #else 19 | template 20 | #endif 21 | class stack { 22 | // 定义友元函数 23 | friend bool operator== __STL_NULL_TMPL_ARGS (const stack&, const stack&); 24 | friend bool operator< __STL_NULL_TMPL_ARGS (const stack&, const stack&); 25 | public: 26 | typedef typename Sequence::value_type value_type; 27 | typedef typename Sequence::size_type size_type; 28 | typedef typename Sequence::reference reference; 29 | typedef typename Sequence::const_reference const_reference; 30 | protected: 31 | Sequence c; 32 | public: 33 | bool empty() const { return c.empty(); } // 调用deque的empty函数 34 | size_type size() const { return c.size(); } 35 | // 调用deque的back函数 36 | reference top() { return c.back(); } 37 | const_reference top() const { return c.back(); } 38 | // 只封装了push_back和pop_back函数, 只对尾进行操作 39 | void push(const value_type& x) { c.push_back(x); } 40 | void pop() { c.pop_back(); } 41 | }; 42 | 43 | // 这里调用的deque的==与!=重载 44 | template 45 | bool operator==(const stack& x, const stack& y) { 46 | return x.c == y.c; 47 | } 48 | template 49 | bool operator<(const stack& x, const stack& y) { 50 | return x.c < y.c; 51 | } 52 | ``` 53 | 54 | 55 | 56 | ### 总结 57 | 58 | 这里stack严格来说是配接器, 就是修改其他容器的接口实现简单的功能. 同样下节分析的`queue`也是以`deque`为接口的配接器. 59 | 60 | -------------------------------------------------------------------------------- /21 queue.md: -------------------------------------------------------------------------------- 1 | # queue 2 | 3 | ### 前言 4 | 5 | 上一节分析了`stack`实现, `stack`是修改了`deque`的接口而实现的一个功能简单的结构, 本节分析的`queue`也是用`deque`为底层容器封装. 6 | 7 | `queue`数据都是在头部进行操作的, 之允许进行push和pop操作. 8 | 9 | 10 | 11 | ### queue分析 12 | 13 | **queue结构** 14 | 15 | ```c++ 16 | #ifndef __STL_LIMITED_DEFAULT_TEMPLATES 17 | template > 18 | #else 19 | template 20 | #endif 21 | class queue { 22 | // 定义友元函数 23 | friend bool operator== __STL_NULL_TMPL_ARGS (const queue& x, const queue& y); 24 | friend bool operator< __STL_NULL_TMPL_ARGS (const queue& x, const queue& y); 25 | public: 26 | typedef typename Sequence::value_type value_type; 27 | typedef typename Sequence::size_type size_type; 28 | typedef typename Sequence::reference reference; 29 | typedef typename Sequence::const_reference const_reference; 30 | protected: 31 | Sequence c; 32 | public: 33 | bool empty() const { return c.empty(); } 34 | size_type size() const { return c.size(); } 35 | // 调用deque的front函数 36 | reference front() { return c.front(); } 37 | const_reference front() const { return c.front(); } 38 | reference back() { return c.back(); } 39 | const_reference back() const { return c.back(); } 40 | // 只封装push_back, pop_front函数 41 | void push(const value_type& x) { c.push_back(x); } 42 | void pop() { c.pop_front(); } 43 | }; 44 | ``` 45 | 46 | 47 | 48 | **友元函数** 49 | 50 | ```c++ 51 | // 实现重载 52 | template 53 | bool operator==(const queue& x, const queue& y) { 54 | return x.c == y.c; 55 | } 56 | 57 | template 58 | bool operator<(const queue& x, const queue& y) { 59 | return x.c < y.c; 60 | } 61 | ``` 62 | 63 | 64 | 65 | ### queue实际操作 66 | 67 | ```c++ 68 | int main() 69 | { 70 | std::queue qu; 71 | qu.push(1); 72 | qu.push(2); 73 | qu.push(3); 74 | qu.size(); 75 | while(!qu.empty()) 76 | { 77 | std::cout << qu.front() << " "; // 1 2 3 78 | qu.pop(); 79 | } 80 | exit(0); 81 | } 82 | ``` 83 | 84 | 85 | 86 | ### 总结 87 | 88 | `queue`与`stack`都是使用底层接口封装的结构, 他们是被称为配接器而不是容器. -------------------------------------------------------------------------------- /22 heap.md: -------------------------------------------------------------------------------- 1 | # heap最大堆 2 | 3 | ### 前言 4 | 5 | 在分析本节之前你至少应该对堆排序有所了解, 大根堆, 小根堆等. 本节分析的`heap`就是堆排序, 严格意义上来讲heap并不是一个容器, 所以他没有实现自己的迭代器, 也就没有遍历操作, 它只是一种算法. 代码来自stl_heap.h. 6 | 7 | 8 | 9 | ### heap分析 10 | 11 | 12 | 13 | #### push插入元素 14 | 15 | 插入函数是`push_heap`. `heap`只接受`RandomAccessIterator`类型的迭代器. 16 | 17 | 注意, 在分析heap的时候最好还是自己画一次, 画一个数组一个二叉树. 18 | 19 | ```c++ 20 | template 21 | inline void push_heap(RandomAccessIterator first, RandomAccessIterator last) { 22 | __push_heap_aux(first, last, distance_type(first), value_type(first)); 23 | } 24 | 25 | template 26 | inline void __push_heap_aux(RandomAccessIterator first, RandomAccessIterator last, Distance*, T*) 27 | { 28 | // 这里传入的是两个迭代器的长度, 0, 还有最后一个数据 29 | __push_heap(first, Distance((last - first) - 1), Distance(0), T(*(last - 1))); 30 | } 31 | ``` 32 | 33 | **push的核心代码** 34 | 35 | ```c++ 36 | template 37 | void __push_heap(RandomAccessIterator first, Distance holeIndex,Distance topIndex, T value) 38 | { 39 | // 这里就行二分, 因为二叉树每一行都是2的倍数 40 | Distance parent = (holeIndex - 1) / 2; 41 | // 这里判断的是当前没有达到堆顶并且传入的值大于根节点的值, 那就将根节点下移 42 | while (holeIndex > topIndex && *(first + parent) < value) { 43 | // 将根节点下移 44 | *(first + holeIndex) = *(first + parent); 45 | holeIndex = parent; 46 | parent = (holeIndex - 1) / 2; 47 | } 48 | 49 | // 将数组插入到合适的位置, 可能是根也可能是叶 50 | *(first + holeIndex) = value; 51 | } 52 | ``` 53 | 54 | 55 | 56 | #### pop弹出元素 57 | 58 | pop操作其实并没有真正意义去删除数据, 而是将数据放在最后, 只是没有指向最后的元素而已, 这里arrary也可以使用, 毕竟没有对数组的大小进行调整. pop的实现有两种, 这里都罗列了出来, 另一个传入的是cmp伪函数. 59 | 60 | ```c++ 61 | template 62 | inline void pop_heap(RandomAccessIterator first, RandomAccessIterator last, 63 | Compare comp) { 64 | __pop_heap_aux(first, last, value_type(first), comp); 65 | } 66 | template 67 | inline void __pop_heap_aux(RandomAccessIterator first, 68 | RandomAccessIterator last, T*, Compare comp) { 69 | __pop_heap(first, last - 1, last - 1, T(*(last - 1)), comp, 70 | distance_type(first)); 71 | } 72 | template 73 | inline void __pop_heap(RandomAccessIterator first, RandomAccessIterator last, 74 | RandomAccessIterator result, T value, Compare comp, 75 | Distance*) { 76 | *result = *first; 77 | __adjust_heap(first, Distance(0), Distance(last - first), value, comp); 78 | } 79 | template 80 | inline void __pop_heap(RandomAccessIterator first, RandomAccessIterator last, 81 | RandomAccessIterator result, T value, Distance*) { 82 | *result = *first; // 因为这里是大根堆, 所以first的值就是最大值, 先将最大值保存. 83 | __adjust_heap(first, Distance(0), Distance(last - first), value); 84 | } 85 | 86 | ``` 87 | 88 | **pop的核心函数**. 这里主要之分析第一个版本. 89 | 90 | pop弹出的是二叉树的最下一排的数据. 91 | 92 | ```c++ 93 | template 94 | void __adjust_heap(RandomAccessIterator first, Distance holeIndex, Distance len, T value) 95 | { 96 | // holeIndex传入的是0 97 | Distance topIndex = holeIndex; 98 | // secondChild是右孩子的一个节点 99 | Distance secondChild = 2 * holeIndex + 2; 100 | while (secondChild < len) { 101 | // 比较左右节点, 根节点较下就将根节点下移, 比较大的节点上移 102 | if (*(first + secondChild) < *(first + (secondChild - 1))) 103 | secondChild--; 104 | *(first + holeIndex) = *(first + secondChild); 105 | holeIndex = secondChild; 106 | // 下一个左右节点 107 | secondChild = 2 * (secondChild + 1); 108 | } 109 | if (secondChild == len) { 110 | // 没有右节点就找左节点并且上移 111 | *(first + holeIndex) = *(first + (secondChild - 1)); 112 | holeIndex = secondChild - 1; 113 | } 114 | // 重新调整堆 115 | __push_heap(first, holeIndex, topIndex, value); 116 | } 117 | // cmpare版本只将比较修改成用户定义的函数 118 | template 119 | void __adjust_heap(RandomAccessIterator first, Distance holeIndex, 120 | Distance len, T value, Compare comp) { 121 | Distance topIndex = holeIndex; 122 | Distance secondChild = 2 * holeIndex + 2; 123 | while (secondChild < len) { 124 | if (comp(*(first + secondChild), *(first + (secondChild - 1)))) 125 | secondChild--; 126 | *(first + holeIndex) = *(first + secondChild); 127 | holeIndex = secondChild; 128 | secondChild = 2 * (secondChild + 1); 129 | } 130 | if (secondChild == len) { 131 | *(first + holeIndex) = *(first + (secondChild - 1)); 132 | holeIndex = secondChild - 1; 133 | } 134 | __push_heap(first, holeIndex, topIndex, value, comp); 135 | } 136 | ``` 137 | 138 | **make_heap函数, 将数组变为堆存放**. 139 | 140 | ```c++ 141 | template 142 | inline void make_heap(RandomAccessIterator first, RandomAccessIterator last) { 143 | __make_heap(first, last, value_type(first), distance_type(first)); 144 | } 145 | template 146 | void __make_heap(RandomAccessIterator first, RandomAccessIterator last, T*, 147 | Distance*) { 148 | if (last - first < 2) return; 149 | // 计算长度, 并找出中间的根值 150 | Distance len = last - first; 151 | Distance parent = (len - 2)/2; 152 | 153 | while (true) { 154 | // 一个个进行调整, 放到后面 155 | __adjust_heap(first, parent, len, T(*(first + parent))); 156 | if (parent == 0) return; 157 | parent--; 158 | } 159 | } 160 | ``` 161 | 162 | **sort, 堆排序其实就是每次将第一位数据弹出从而实现排序功能**. 163 | 164 | ```c++ 165 | template 166 | void sort_heap(RandomAccessIterator first, RandomAccessIterator last) { 167 | while (last - first > 1) pop_heap(first, last--); 168 | } 169 | template 170 | void sort_heap(RandomAccessIterator first, RandomAccessIterator last, 171 | Compare comp) { 172 | while (last - first > 1) pop_heap(first, last--, comp); 173 | } 174 | ``` 175 | 176 | 177 | 178 | ### 总结 179 | 180 | `heap`没有自己的迭代器,只要支持`RandomAccessIterator`的容器都可以作为Heap容器. `heap`最重要的函数还是`pop`和`push`的实现. -------------------------------------------------------------------------------- /23 priority_queue.md: -------------------------------------------------------------------------------- 1 | # priority_queue 2 | 3 | ### 前言 4 | 5 | 上一节分析`heap`其实就是为`priority_queue`做准备. `priority_queue`是一个优先级队列, 是带权值的. 支持插入和删除操作, 其只能从尾部插入,头部删除, 并且其顺序也并非是根据加入的顺序排列的. `priority_queue`因为也是队列的一种体现, 所以也就跟队列一样不能直接的遍历数组, 也就没有迭代器. `priority_queue`本身也不算是一个容器, 它是以`vector`为容器以`heap`为数据操作的**配置器**. 6 | 7 | 8 | 9 | ### 源码分析 10 | 11 | 12 | 13 | #### 类型定义 14 | 15 | ```c++ 16 | #ifndef __STL_LIMITED_DEFAULT_TEMPLATES 17 | template , 18 | class Compare = less > 19 | #else 20 | template 21 | #endif 22 | class priority_queue { 23 | public: 24 | // 符合traits编程规范 25 | typedef typename Sequence::value_type value_type; 26 | typedef typename Sequence::size_type size_type; 27 | typedef typename Sequence::reference reference; 28 | typedef typename Sequence::const_reference const_reference; 29 | protected: 30 | Sequence c; // 定义vector容器的对象 31 | Compare comp; // 定义比较函数(伪函数) 32 | ... 33 | }; 34 | ``` 35 | 36 | 37 | 38 | #### 构造函数 39 | 40 | ```c++ 41 | class priority_queue { 42 | ... 43 | public: 44 | priority_queue() : c() {} // 默认构造函数 45 | explicit priority_queue(const Compare& x) : c(), comp(x) {} // 设置伪函数 46 | 47 | #ifdef __STL_MEMBER_TEMPLATES 48 | // 接受以迭代器类型的参数 49 | // 接受两个迭代器以及函数. 传入的迭代器范围内表示的元素以comp定义的方式进行调整 50 | template 51 | priority_queue(InputIterator first, InputIterator last, const Compare& x) 52 | : c(first, last), comp(x) { make_heap(c.begin(), c.end(), comp); } 53 | // 接受两个迭代器. 传入的迭代器范围内表示的元素以默认的大根堆进行调整 54 | template 55 | priority_queue(InputIterator first, InputIterator last) 56 | : c(first, last) { make_heap(c.begin(), c.end(), comp); } 57 | #else /* __STL_MEMBER_TEMPLATES */ 58 | // 接受两个迭代器以及函数. 传入的迭代器范围内表示的元素以comp定义的方式进行调整 59 | priority_queue(const value_type* first, const value_type* last, 60 | const Compare& x) : c(first, last), comp(x) { 61 | make_heap(c.begin(), c.end(), comp); 62 | } 63 | // 接受两个迭代器. 传入的迭代器范围内表示的元素以默认的大根堆进行调整 64 | priority_queue(const value_type* first, const value_type* last) 65 | : c(first, last) { make_heap(c.begin(), c.end(), comp); } 66 | #endif /* __STL_MEMBER_TEMPLATES */ 67 | ... 68 | }; 69 | ``` 70 | 71 | 72 | 73 | #### 属性获取 74 | 75 | `priority_queue`只有简单的3个属性获取的函数, 其本身的操作也很简单, 只是实现依赖了`vector`和`heap`就变得比较复杂. 76 | 77 | ```c++ 78 | class priority_queue { 79 | ... 80 | public: 81 | bool empty() const { return c.empty(); } 82 | size_type size() const { return c.size(); } 83 | const_reference top() const { return c.front(); } 84 | ... 85 | }; 86 | ``` 87 | 88 | #### push和pop实现 89 | 90 | push和pop具体都是采用的`heap`算法. 91 | 92 | ```c++ 93 | class priority_queue { 94 | ... 95 | public: 96 | void push(const value_type& x) { 97 | __STL_TRY { 98 | c.push_back(x); 99 | // 间接使用heap算法 100 | push_heap(c.begin(), c.end(), comp); 101 | } 102 | __STL_UNWIND(c.clear()); 103 | } 104 | void pop() { 105 | __STL_TRY { 106 | // 间接使用heap算法 107 | pop_heap(c.begin(), c.end(), comp); 108 | c.pop_back(); 109 | } 110 | __STL_UNWIND(c.clear()); 111 | } 112 | }; 113 | ``` 114 | 115 | 116 | 117 | ### 实际操作 118 | 119 | ```c 120 | int main() 121 | { 122 | int a[4] = { 1, 2, 3, 4 }; 123 | priority_queue pq(a, a+4); 124 | while(!pq.empty()) 125 | { 126 | cout << pq.top() << " "; // 4, 3, 2, 1 127 | pq.pop(); 128 | } 129 | 130 | exit(0); 131 | } 132 | ``` 133 | 134 | 135 | 136 | ### 总结 137 | 138 | `priority_queue`本身实现是很复杂的, 但是当我们已经了解过`vector`, `heap`之后再来看, 他其实很简单了, 就是将vector作为容器, heap作为算法来操作的配置器, 这也体现了STL的灵活性是很高的, 通过各个容器与算法的结合就能做出另一种功能的结构. 139 | 140 | -------------------------------------------------------------------------------- /28 set.md: -------------------------------------------------------------------------------- 1 | # set 2 | 3 | ### 前言 4 | 5 | 上面两节我们分析了`RB-tree`, 同时我们也知道了rb-tree的插入操作还分为可重复插入和不可重复插入(insert_unique). 本节分析set, 严格意义说`set`就是修改了底层容器接口的, 所以应该是配置器. `set`就是将`RB-tree`作为底层容器, 以`insert_unique`为核心的配接器. 6 | 7 | 8 | 9 | ### set操作 10 | 11 | 下面是对set的构造和insert的简单操作, 这里重复插入one, 但是重复的数据只能被插入一次, 并且不能被修改. 12 | 13 | ```c++ 14 | int main() 15 | { 16 | set str; 17 | str.insert("zero"); 18 | 19 | str.insert("one"); 20 | str.insert("one"); 21 | 22 | str.insert("two"); 23 | str.insert("three"); 24 | set str1(str.begin(), str.end()); 25 | for(const auto &i : str1) 26 | cout << i << " "; // one three two zero 27 | cout << endl; 28 | cout << *str.begin() << " " << str.count("one") << " " << *str.find("one") << " " << endl; // one 1 one 29 | 30 | exit(0); 31 | } 32 | ``` 33 | 34 | 35 | 36 | ### set分析 37 | 38 | `set`的主要实现大都是调用`RB-tree`的接口. 39 | 40 | 41 | 42 | #### set类型定义 43 | 44 | 这里的类型的定义要注意一点, 都是`const`类型, 因为set的主键定义后就不能被修改了, 所以这里都是以`const`类型. 45 | 46 | ```c++ 47 | #ifndef __STL_LIMITED_DEFAULT_TEMPLATES 48 | template , class Alloc = alloc> 49 | #else 50 | template 51 | #endif 52 | class set { 53 | public: 54 | // typedefs: 55 | typedef Key key_type; 56 | typedef Key value_type; 57 | typedef Compare key_compare; 58 | typedef Compare value_compare; 59 | private: 60 | // 一RB-tree为接口封装 61 | typedef rb_tree, key_compare, Alloc> rep_type; 62 | rep_type t; // red-black tree representing set 63 | public: 64 | // 定义的类型都是const类型, 不能修改 65 | typedef typename rep_type::const_pointer pointer; 66 | typedef typename rep_type::const_pointer const_pointer; 67 | typedef typename rep_type::const_reference reference; 68 | typedef typename rep_type::const_reference const_reference; 69 | typedef typename rep_type::const_iterator iterator; 70 | typedef typename rep_type::const_iterator const_iterator; 71 | typedef typename rep_type::const_reverse_iterator reverse_iterator; 72 | typedef typename rep_type::const_reverse_iterator const_reverse_iterator; 73 | typedef typename rep_type::size_type size_type; 74 | typedef typename rep_type::difference_type difference_type; 75 | ... 76 | }; 77 | ``` 78 | 79 | 80 | 81 | #### 构造函数 82 | 83 | 构造函数构造成员的时候调用的是RB-tree的`insert_unique` 84 | 85 | ```c++ 86 | class set { 87 | public: 88 | ... 89 | set() : t(Compare()) {} 90 | explicit set(const Compare& comp) : t(comp) {} // 不能隐式转换 91 | 92 | // 接受两个迭代器 93 | // 构造函数构造成员的时候调用的是RB-tree的insert_unique 94 | template 95 | set(InputIterator first, InputIterator last) 96 | : t(Compare()) { t.insert_unique(first, last); } 97 | template 98 | set(InputIterator first, InputIterator last, const Compare& comp) 99 | : t(comp) { t.insert_unique(first, last); } 100 | 101 | set(const value_type* first, const value_type* last) 102 | : t(Compare()) { t.insert_unique(first, last); } 103 | set(const value_type* first, const value_type* last, const Compare& comp) 104 | : t(comp) { t.insert_unique(first, last); } 105 | 106 | set(const_iterator first, const_iterator last) 107 | : t(Compare()) { t.insert_unique(first, last); } 108 | set(const_iterator first, const_iterator last, const Compare& comp) 109 | : t(comp) { t.insert_unique(first, last); } 110 | ... 111 | }; 112 | ``` 113 | 114 | 115 | 116 | #### 成员属性获取 117 | 118 | ```c++ 119 | class set { 120 | public: 121 | ... 122 | // 所有的操作都是通过调用RB-tree获取的 123 | key_compare key_comp() const { return t.key_comp(); } 124 | value_compare value_comp() const { return t.key_comp(); } 125 | iterator begin() const { return t.begin(); } 126 | iterator end() const { return t.end(); } 127 | reverse_iterator rbegin() const { return t.rbegin(); } 128 | reverse_iterator rend() const { return t.rend(); } 129 | bool empty() const { return t.empty(); } 130 | size_type size() const { return t.size(); } 131 | size_type max_size() const { return t.max_size(); } 132 | // 交换 133 | void swap(set& x) { t.swap(x.t); } 134 | 135 | // 其他的find, count等都是直接调用的RB-tree的接口 136 | iterator find(const key_type& x) const { return t.find(x); } 137 | size_type count(const key_type& x) const { return t.count(x); } 138 | iterator lower_bound(const key_type& x) const { 139 | return t.lower_bound(x); 140 | } 141 | iterator upper_bound(const key_type& x) const { 142 | return t.upper_bound(x); 143 | } 144 | pair equal_range(const key_type& x) const { 145 | return t.equal_range(x); 146 | } 147 | ... 148 | }; 149 | template 150 | inline void swap(set& x, set& y) 151 | { 152 | x.swap(y); 153 | } 154 | ``` 155 | 156 | 157 | 158 | #### insert 159 | 160 | ```c++ 161 | class set { 162 | public: 163 | ... 164 | // pair类型我们准备下一节分析, 这里是直接调用insert_unique, 返回插入成功就是pair( , true), 插入失败则是( , false) 165 | typedef pair pair_iterator_bool; 166 | pair insert(const value_type& x) { 167 | pair p = t.insert_unique(x); 168 | return pair(p.first, p.second); 169 | } 170 | // 指定位置的插入 171 | iterator insert(iterator position, const value_type& x) { 172 | typedef typename rep_type::iterator rep_iterator; 173 | return t.insert_unique((rep_iterator&)position, x); 174 | } 175 | // 可接受范围插入 176 | template 177 | void insert(InputIterator first, InputIterator last) { 178 | t.insert_unique(first, last); 179 | } 180 | ... 181 | }; 182 | ``` 183 | 184 | 185 | 186 | #### erase 187 | 188 | ```c++ 189 | class set { 190 | public: 191 | ... 192 | // erase的实现是通过调用RB-tree实现的erase 193 | void erase(iterator position) { 194 | typedef typename rep_type::iterator rep_iterator; 195 | t.erase((rep_iterator&)position); 196 | } 197 | size_type erase(const key_type& x) { 198 | return t.erase(x); 199 | } 200 | void erase(iterator first, iterator last) { 201 | typedef typename rep_type::iterator rep_iterator; 202 | t.erase((rep_iterator&)first, (rep_iterator&)last); 203 | } 204 | void clear() { t.clear(); } 205 | ... 206 | }; 207 | ``` 208 | 209 | 210 | 211 | #### 重载 212 | 213 | 重载运算符也是以`RB-tree`为接口调用. 214 | 215 | ```c++ 216 | class set { 217 | public: 218 | ... 219 | 220 | set(const set& x) : t(x.t) {} 221 | set& operator=(const set& x) { 222 | t = x.t; 223 | return *this; 224 | } 225 | ... 226 | }; 227 | 228 | template 229 | inline bool operator==(const set& x, 230 | const set& y) { 231 | return x.t == y.t; 232 | } 233 | 234 | template 235 | inline bool operator<(const set& x, 236 | const set& y) { 237 | return x.t < y.t; 238 | } 239 | ``` 240 | 241 | 242 | 243 | ### 总结 244 | 245 | 本节简单的分析了`set`主要是以`RB-tree`接口实现的配置器, `set`每个元素被初始化之后就不能进行修改, 这都是set定义类型的时候都是`const`类型 -------------------------------------------------------------------------------- /29 pair.md: -------------------------------------------------------------------------------- 1 | # pair 2 | 3 | ### 前言 4 | 5 | 前面在分析set, RB-tree都有在insert实现中出现`pair`, 下节分析`map`的时候更会经常出现pair, 所以打算在之前先对pair有个认识. 6 | 7 | `pair`是一个有两个变量的结构体, 即谁都可以直接调用它的变量, 毕竟struct默认权限都是public, 将两个变量用`pair`绑定在一起, 这就为map提供的存储的基础. 8 | 9 | 10 | 11 | ### pair操作 12 | 13 | pair结构是在`map`头文件里面, 直接使用pair实例化可以修改任意元素, 而map对`first`设置为了`const`, 所以不能修改键值. 14 | 15 | ```c++ 16 | int main() 17 | { 18 | pair pa[3]; 19 | pa[0].first = "one", pa[0].second = 1; 20 | pa[1].first = "two", pa[1].second = 2; 21 | pa[2].first = "three", pa[2].second = 3; 22 | 23 | for(const auto &i : pa) 24 | cout << i.first << " " << i.second << endl; 25 | // one 1 26 | // two 2 27 | // three 3 28 | 29 | pa[0].first = "zero", pa[0].second = 0; 30 | for(const auto &i : pa) 31 | cout << i.first << " " << i.second << endl; 32 | // zero 0 33 | // two 2 34 | // three 3 35 | 36 | exit(0); 37 | } 38 | ``` 39 | 40 | 41 | 42 | 43 | 44 | ### pair分析 45 | 46 | pair的实现很简单, 结构体封装了两个变量, 重载了==与<运算符, 也提供了构造函数. 47 | 48 | 49 | 50 | **pair结构定义** 51 | 52 | ```c++ 53 | template // 两个参数类型 54 | struct pair { 55 | typedef T1 first_type; 56 | typedef T2 second_type; 57 | 58 | // 定义的两个变量 59 | T1 first; 60 | T2 second; 61 | 62 | // 构造函数 63 | pair() : first(T1()), second(T2()) {} 64 | pair(const T1& a, const T2& b) : first(a), second(b) {} 65 | #ifdef __STL_MEMBER_TEMPLATES 66 | template 67 | pair(const pair& p) : first(p.first), second(p.second) {} 68 | #endif 69 | }; 70 | ``` 71 | 72 | 73 | 74 | **重载** 75 | 76 | 因为只有两个变量, 运算符重载也很简单 77 | 78 | ```c++ 79 | template 80 | inline bool operator==(const pair& x, const pair& y) { 81 | return x.first == y.first && x.second == y.second; 82 | } 83 | template 84 | inline bool operator<(const pair& x, const pair& y) { 85 | return x.first < y.first || (!(y.first < x.first) && x.second < y.second); 86 | } 87 | ``` 88 | 89 | **make_pair** 90 | 91 | 根据两个数值构造一个pair. 92 | 93 | ```c++ 94 | template 95 | inline pair make_pair(const T1& x, const T2& y) { 96 | return pair(x, y); 97 | } 98 | ``` 99 | 100 | 101 | 102 | ### 总结 103 | 104 | 整体pair的功能与实现都是很简单的, 这都是为map的实现做准备的, 下节我们就来分析map的实现. -------------------------------------------------------------------------------- /3 第二级配置器.md: -------------------------------------------------------------------------------- 1 | # 第二级配置器 2 | 3 | ### 前言 4 | 5 | 第一级是直接调用`malloc`分配空间, 调用`free`释放空间, 第二级三就是建立一个内存池, 小于128字节的申请都直接在内存池申请, 不直接调用`malloc`和`free`. 本节分析第二级空间配置器, STL将第二级配置器设置为默认的配置器, 所以只要一次申请的空间不超过128字节就默认在内存池中申请空间, 超过才会调用第一级配置器. 6 | 7 | 8 | 9 | ### 第二级配置器 10 | 11 | 首先先来介绍3个常量. 12 | 13 | > 1. `__ALIGN` : 以8字节进行对齐 14 | > 2. `__MAX_BYTES` : 二级分配器最大分配的内存大小 15 | > 3. `__NFREELISTS` : 128字节能分配的的链表个数, 并且从每个链表保存的内存大小都是8的倍数, 而且都比前一个大8字节, 也就是分别是8, 16, 32...128字节 16 | 17 | ```c++ 18 | // 二级配置器 19 | enum {__ALIGN = 8}; // 设置对齐要求. 对齐为8字节, 没有8字节自动补齐 20 | enum {__MAX_BYTES = 128}; // 第二级配置器的最大一次性申请大小, 大于128就直接调用第一级配置器 21 | enum {__NFREELISTS = __MAX_BYTES/__ALIGN}; // 链表个数, 分别代表8, 16, 32....字节的链表 22 | ``` 23 | 24 | 再介绍一个宏操作, 这是进行对齐操作, 将不满8的倍数的填充成8的倍数. 25 | 26 | ```c++ 27 | static size_t FREELIST_INDEX(size_t bytes) \ 28 | {\ 29 | return (((bytes) + ALIGN-1) / __ALIGN - 1);\ 30 | } 31 | ``` 32 | 33 | 34 | 35 | #### 从allocate先切入分析 36 | 37 | 1. 先判断申请的字节大小是不是大于128字节, 是, 则交给第一级配置器来处理. 否, 继续往下执行 38 | 2. 找到分配的地址对齐后分配的是第几个大小的链表. 39 | 3. 获得该链表指向的首地址, 如果链表没有多余的内存, 就先填充链表. 40 | 4. 返回链表的首地址, 和一块能容纳一个对象的内存, 并更新链表的首地址 41 | 42 | ```c++ 43 | static void * allocate(size_t n) 44 | { 45 | obj * __VOLATILE * my_free_list; 46 | obj * __RESTRICT result; 47 | 48 | if (n > (size_t) __MAX_BYTES) 49 | { 50 | return(malloc_alloc::allocate(n)); 51 | } 52 | my_free_list = free_list + FREELIST_INDEX(n); 53 | result = *my_free_list; 54 | if (result == 0) // 没有多余的内存, 就先填充链表. 55 | { 56 | void *r = refill(ROUND_UP(n)); 57 | return r; 58 | } 59 | *my_free_list = result -> free_list_link; 60 | return (result); 61 | }; 62 | ``` 63 | 64 | `refill`内存填充. 65 | 66 | > 1. 向内存池申请空间的起始地址 67 | > 2. 如果只申请到一个对象的大小, 就直接返回一个内存的大小, 如果有更多的内存, 就继续执行 68 | > 3. 从第二个块内存开始, 把从内存池里面分配的内存用链表给串起来, 并返回一个块内存的地址给用户 69 | 70 | ```c++ 71 | // 内存填充 72 | template 73 | void* __default_alloc_template::refill(size_t n) 74 | { 75 | int nobjs = 20; 76 | char * chunk = chunk_alloc(n, nobjs); // 向内存池申请空间的起始地址 77 | obj * __VOLATILE * my_free_list; 78 | obj * result; 79 | obj * current_obj, * next_obj; 80 | int i; 81 | 82 | // 如果只申请到一个对象的大小, 就直接返回一个内存的大小 83 | if (1 == nobjs) return(chunk); 84 | my_free_list = free_list + FREELIST_INDEX(n); 85 | 86 | // 申请的大小不只一个对象的大小的时候 87 | result = (obj *)chunk; 88 | // my_free_list指向内存池返回的地址的下一个对齐后的地址 89 | *my_free_list = next_obj = (obj *)(chunk + n); 90 | // 这里从第二个开始的原因主要是第一块地址返回给了用户, 现在需要把从内存池里面分配的内存用链表给串起来 91 | for (i = 1; ; i++) 92 | { 93 | current_obj = next_obj; 94 | next_obj = (obj *)((char *)next_obj + n); 95 | if (nobjs - 1 == i) 96 | { 97 | current_obj -> free_list_link = 0; 98 | break; 99 | } 100 | else 101 | { 102 | current_obj -> free_list_link = next_obj; 103 | } 104 | } 105 | return(result); 106 | } 107 | ``` 108 | 109 | 110 | 111 | #### 再从deallocate结束 112 | 113 | > 1. 释放的内存大于128字节直接调用一级配置器进行释放 114 | > 2. 将内存直接还给对应大小的链表就行了, 并不用直接释放内存, 以便后面分配内存的时候快速. 115 | 116 | ```c++ 117 | static void deallocate(void *p, size_t n) 118 | { 119 | obj *q = (obj *)p; 120 | obj * __VOLATILE * my_free_list; 121 | 122 | // 释放的内存大于128字节直接调用一级配置器进行释放 123 | if (n > (size_t) __MAX_BYTES) 124 | { 125 | malloc_alloc::deallocate(p, n); 126 | return; 127 | } 128 | my_free_list = free_list + FREELIST_INDEX(n); 129 | q -> free_list_link = *my_free_list; 130 | *my_free_list = q; 131 | } 132 | ``` 133 | 134 | 135 | 136 | #### 统一的接口 137 | 138 | 定义符合STL规格的配置器接口, 不管是一级配置器还是二级配置器都是使用这个接口进行分配的 139 | 140 | ```c++ 141 | // 定义符合STL规格的配置器接口, 不管是一级配置器还是二级配置器都是使用这个接口进行分配的 142 | template 143 | class simple_alloc { 144 | public: 145 | static T *allocate(size_t n) 146 | { return 0 == n? 0 : (T*) Alloc::allocate(n * sizeof (T)); } 147 | static T *allocate(void) 148 | { return (T*) Alloc::allocate(sizeof (T)); } 149 | static void deallocate(T *p, size_t n) 150 | { if (0 != n) Alloc::deallocate(p, n * sizeof (T)); } 151 | static void deallocate(T *p) 152 | { Alloc::deallocate(p, sizeof (T)); } 153 | }; 154 | ``` 155 | 156 | 157 | 158 | --- 159 | 160 | ### 总结 161 | 162 | 用链表来保存不同字节大小的内存块, 就很容易的进行维护, 而且每次的内存分配都直接可以从链表或者内存池中获得, 提升了我们申请内存的效率, 毕竟每次调用malloc和free效率是很低的, 特别是很小内存的时候. 163 | 164 | **STL默认的就是第二级配置器, 它会自动判断我们使用哪一个配置器.** -------------------------------------------------------------------------------- /30 map.md: -------------------------------------------------------------------------------- 1 | # map 2 | 3 | ### 前言 4 | 5 | 上一节分析了pair结构, 正是为`map`分析做铺垫, map本身实现也不难, 其数据存储是pair, 存储结构是RB-tree, 即map也并不能说是关联容器, 而应该是配接器. 6 | 7 | 8 | 9 | ### map操作 10 | 11 | map的insert必须是以pair为存储结构, 当然也可以直接使用make_pair构造一个临时pair, 这个函数我们上节分析pair的时候讲过. 12 | 13 | ```c++ 14 | int main() 15 | { 16 | map m; 17 | pair p; 18 | p.first = "zero", p.second = 0; 19 | m.insert(p); 20 | m.insert(make_pair("one", 1)); 21 | 22 | if(!m.empty()) 23 | { 24 | cout << m["one"] << " " << m["two"] << endl; // 1 0 25 | cout << (*m.find("one")).first << " " << (*m.find("one")).second << endl; // one 1 26 | } 27 | 28 | exit(0); 29 | } 30 | ``` 31 | 32 | 上面唯一比较复杂的就是`(*m.find("one")).second`的操作, 这在我们下面分析重载`[]`时会具体分析, 下面我们就来分析map吧. 33 | 34 | 35 | 36 | ### map分析 37 | 38 | **map基本结构定义** 39 | 40 | map对象实例化`map`, 键值是不能直接修改的, 而数据可以修改. 41 | 42 | ```c++ 43 | #ifndef __STL_LIMITED_DEFAULT_TEMPLATES 44 | template , class Alloc = alloc> 45 | #else 46 | template 47 | #endif 48 | class map { 49 | public: 50 | typedef Key key_type; // 定义键值 51 | typedef T data_type; // 定义数据 52 | typedef T mapped_type; 53 | typedef pair value_type; // 这里定义了map的数据类型为pair, 且键值为const类型, 不能修改 54 | typedef Compare key_compare; 55 | 56 | private: 57 | typedef rb_tree, key_compare, Alloc> rep_type; // 定义红黑树, map是以rb-tree结构为基础的 59 | rep_type t; // red-black tree representing map 60 | public: 61 | // 定义类型 62 | typedef typename rep_type::pointer pointer; 63 | typedef typename rep_type::const_pointer const_pointer; 64 | typedef typename rep_type::reference reference; 65 | typedef typename rep_type::const_reference const_reference; 66 | typedef typename rep_type::iterator iterator; 67 | typedef typename rep_type::const_iterator const_iterator; 68 | typedef typename rep_type::reverse_iterator reverse_iterator; 69 | typedef typename rep_type::const_reverse_iterator const_reverse_iterator; 70 | typedef typename rep_type::size_type size_type; 71 | typedef typename rep_type::difference_type difference_type; 72 | ... 73 | }; 74 | ``` 75 | 76 | 77 | 78 | **嵌套类** : 这是一个仿函数, 为键值key提供比较接口 79 | 80 | ```c++ 81 | class map { 82 | public: 83 | ... 84 | // 这是一个仿函数, 为键值key提供比较接口 85 | class value_compare : public binary_function 86 | { 87 | friend class map; 88 | protected : 89 | Compare comp; 90 | value_compare(Compare c) : comp(c) {} 91 | public: 92 | // 重载(), 可进行临时比较 93 | bool operator()(const value_type& x, const value_type& y) const { 94 | return comp(x.first, y.first); 95 | } 96 | }; 97 | ... 98 | }; 99 | ``` 100 | 101 | **构造函数** map的所有插入操作都是调用的是`RB-tree`的`insert_unique`, 不允许出现重复的键 102 | 103 | ```c++ 104 | class map { 105 | public: 106 | ... 107 | public: 108 | // allocation/deallocation 109 | map() : t(Compare()) {} // 默认构造函数 110 | explicit map(const Compare& comp) : t(comp) {} 111 | #ifdef __STL_MEMBER_TEMPLATES 112 | // 接受两个迭代器 113 | template 114 | map(InputIterator first, InputIterator last) 115 | : t(Compare()) { t.insert_unique(first, last); } 116 | template 117 | map(InputIterator first, InputIterator last, const Compare& comp) 118 | : t(comp) { t.insert_unique(first, last); } 119 | #else 120 | map(const value_type* first, const value_type* last) 121 | : t(Compare()) { t.insert_unique(first, last); } 122 | map(const value_type* first, const value_type* last, const Compare& comp) 123 | : t(comp) { t.insert_unique(first, last); } 124 | map(const_iterator first, const_iterator last) 125 | : t(Compare()) { t.insert_unique(first, last); } 126 | map(const_iterator first, const_iterator last, const Compare& comp) 127 | : t(comp) { t.insert_unique(first, last); } 128 | #endif /* __STL_MEMBER_TEMPLATES */ 129 | ... 130 | }; 131 | ``` 132 | 133 | 134 | 135 | **基本类型属性获取** 136 | 137 | ```c++ 138 | class map { 139 | public: 140 | ... 141 | public: 142 | // 实际调用的是RB-tree的key_comp函数 143 | key_compare key_comp() const { return t.key_comp(); } 144 | // value_comp实际返回的是一个仿函数value_compare 145 | value_compare value_comp() const { return value_compare(t.key_comp()); } 146 | // 以下的begin, end等操作都是调用的是RB-tree的接口 147 | iterator begin() { return t.begin(); } 148 | const_iterator begin() const { return t.begin(); } 149 | iterator end() { return t.end(); } 150 | const_iterator end() const { return t.end(); } 151 | reverse_iterator rbegin() { return t.rbegin(); } 152 | const_reverse_iterator rbegin() const { return t.rbegin(); } 153 | reverse_iterator rend() { return t.rend(); } 154 | const_reverse_iterator rend() const { return t.rend(); } 155 | bool empty() const { return t.empty(); } 156 | size_type size() const { return t.size(); } 157 | size_type max_size() const { return t.max_size(); } 158 | // 交换, 调用RB-tree的swap, 实际只交换head和count 159 | void swap(map& x) { t.swap(x.t); } 160 | ... 161 | }; 162 | template 163 | inline void swap(map& x, 164 | map& y) { 165 | x.swap(y); 166 | } 167 | ``` 168 | 169 | 170 | 171 | **重载** 172 | 173 | ```c++ 174 | class map { 175 | public: 176 | ... 177 | public: 178 | map(const map& x) : t(x.t) {} 179 | map& operator=(const map& x) 180 | { 181 | t = x.t; 182 | return *this; 183 | } 184 | ... 185 | }; 186 | template 187 | inline bool operator==(const map& x, 188 | const map& y) { 189 | return x.t == y.t; 190 | } 191 | 192 | template 193 | inline bool operator<(const map& x, 194 | const map& y) { 195 | return x.t < y.t; 196 | } 197 | ``` 198 | 199 | 重载操作重点分析 [] 200 | 201 | 1. insert(value_type(k, T()) : 查找是否存在该键值, 如果存在则返回该`pair`, 不存在这重新构造一该键值并且值为空 202 | 2. *((insert(value_type(k, T()))).first) : `pair`的第一个元素表示指向该元素的迭代器, 第二个元素指的是(false与true)是否存在, `first` 便是取出该迭代器而 ` *` 取出pair. 203 | 3. (*((insert(value_type(k, T()))).first)).second : 取出pair结构中的`second`保存的数据 204 | 205 | ```c++ 206 | class map { 207 | public: 208 | ... 209 | public: 210 | // 1. insert(value_type(k, T()) : 查找是否存在该键值, 如果存在则返回该pair, 不存在这重新构造一该键值并且值为空 211 | // 2. *((insert(value_type(k, T()))).first) : pair的第一个元素表示指向该元素的迭代器, 第二个元素指的是(false与true)是否存在, first 便是取出该迭代器而 * 取出pair. 212 | // 3. (*((insert(value_type(k, T()))).first)).second : 取出pair结构中的second保存的数据 213 | T& operator[](const key_type& k) { 214 | return (*((insert(value_type(k, T()))).first)).second; 215 | } 216 | ... 217 | }; 218 | ``` 219 | 220 | 221 | 222 | map的其他insert, erase, find都是直接调用RB-tree的接口函数实现的, 这里就不直接做分析了. 223 | 224 | 225 | 226 | ### 总结 227 | 228 | 实际map也是以RB-tree为底层接口的配接器, 同时map还以pair结构为存储结构, 当这两个都理解后整个map结构分析也就很轻松了. 229 | 230 | -------------------------------------------------------------------------------- /31 multiset.md: -------------------------------------------------------------------------------- 1 | # multiset 2 | 3 | ### 前言 4 | 5 | 前面也分析过`set`, 并且`set`不能插入相同的键, 本节分析的`multiset`与set不同之处就是他允许插入相同的键. 6 | 7 | 8 | 9 | ### multiset操作 10 | 11 | ```c++ 12 | int main() 13 | { 14 | multiset multi; 15 | // 允许重复插入键 16 | multi.insert("zero"); 17 | multi.insert("zero"); 18 | multi.insert("one"); 19 | multi.insert("one"); 20 | 21 | // 证明重复插入进行了 22 | cout << multi.count("zero") << endl; // 2 23 | 24 | // 将所有键输出 25 | if(!multi.empty()) 26 | for(const auto &i : multi) 27 | cout << i << " "; // one one zero zero 28 | 29 | exit(0); 30 | } 31 | ``` 32 | 33 | 34 | 35 | ### multiset分析 36 | 37 | 38 | 39 | **类型定义** 40 | 41 | ```c++ 42 | #ifndef __STL_LIMITED_DEFAULT_TEMPLATES 43 | template , class Alloc = alloc> 44 | #else 45 | template 46 | #endif 47 | class multiset { 48 | public: 49 | // typedefs: 50 | typedef Key key_type; 51 | typedef Key value_type; 52 | typedef Compare key_compare; 53 | typedef Compare value_compare; 54 | // multiset也是以RB-tree为接口 55 | private: 56 | typedef rb_tree, key_compare, Alloc> rep_type; 58 | rep_type t; // red-black tree representing multiset 59 | public: 60 | // 每个都是const类型, 不允许进行修改 61 | typedef typename rep_type::const_pointer pointer; 62 | typedef typename rep_type::const_pointer const_pointer; 63 | typedef typename rep_type::const_reference reference; 64 | typedef typename rep_type::const_reference const_reference; 65 | typedef typename rep_type::const_iterator iterator; 66 | typedef typename rep_type::const_iterator const_iterator; 67 | typedef typename rep_type::const_reverse_iterator reverse_iterator; 68 | typedef typename rep_type::const_reverse_iterator const_reverse_iterator; 69 | typedef typename rep_type::size_type size_type; 70 | typedef typename rep_type::difference_type difference_type; 71 | ... 72 | }; 73 | ``` 74 | 75 | 76 | 77 | **构造函数** 78 | 79 | 与set不同, multiset是以`insert_equal`为接口, 所以允许插入重复的键值. 80 | 81 | ```c++ 82 | class multiset { 83 | ... 84 | public: 85 | // allocation/deallocation 86 | multiset() : t(Compare()) {} 87 | explicit multiset(const Compare& comp) : t(comp) {} 88 | #ifdef __STL_MEMBER_TEMPLATES 89 | template 90 | multiset(InputIterator first, InputIterator last) 91 | : t(Compare()) { t.insert_equal(first, last); } 92 | template 93 | multiset(InputIterator first, InputIterator last, const Compare& comp) 94 | : t(comp) { t.insert_equal(first, last); } 95 | #else 96 | multiset(const value_type* first, const value_type* last) 97 | : t(Compare()) { t.insert_equal(first, last); } 98 | multiset(const value_type* first, const value_type* last, 99 | const Compare& comp) 100 | : t(comp) { t.insert_equal(first, last); } 101 | multiset(const_iterator first, const_iterator last) 102 | : t(Compare()) { t.insert_equal(first, last); } 103 | multiset(const_iterator first, const_iterator last, const Compare& comp) 104 | : t(comp) { t.insert_equal(first, last); } 105 | #endif /* __STL_MEMBER_TEMPLATES */ 106 | ... 107 | }; 108 | ``` 109 | 110 | 111 | 112 | **基本属性获取** 113 | 114 | ```c++ 115 | class multiset { 116 | ... 117 | public: 118 | key_compare key_comp() const { return t.key_comp(); } 119 | // 返回的是仿函数 120 | value_compare value_comp() const { return t.key_comp(); } 121 | iterator begin() const { return t.begin(); } 122 | iterator end() const { return t.end(); } 123 | reverse_iterator rbegin() const { return t.rbegin(); } 124 | reverse_iterator rend() const { return t.rend(); } 125 | bool empty() const { return t.empty(); } 126 | size_type size() const { return t.size(); } 127 | size_type max_size() const { return t.max_size(); } 128 | // 交换 129 | void swap(multiset& x) { t.swap(x.t); } 130 | ... 131 | }; 132 | template 133 | inline void swap(multiset& x, 134 | multiset& y) { 135 | x.swap(y); 136 | } 137 | ``` 138 | 139 | 140 | 141 | **重载** 与set一样 142 | 143 | ```c++ 144 | class multiset { 145 | ... 146 | public: 147 | multiset(const multiset& x) : t(x.t) {} 148 | multiset& 149 | operator=(const multiset& x) { 150 | t = x.t; 151 | return *this; 152 | } 153 | friend bool operator== __STL_NULL_TMPL_ARGS (const multiset&, const multiset&); 154 | friend bool operator< __STL_NULL_TMPL_ARGS (const multiset&, const multiset&); 155 | }; 156 | template 157 | inline bool operator==(const multiset& x, 158 | const multiset& y) { 159 | return x.t == y.t; 160 | } 161 | template 162 | inline bool operator<(const multiset& x, 163 | const multiset& y) { 164 | return x.t < y.t; 165 | } 166 | ``` 167 | 168 | 169 | 170 | **insert** 171 | 172 | 以RB-tree的`insert_equal`为接口. 允许重复插入 173 | 174 | ```c++ 175 | class multiset { 176 | ... 177 | public: 178 | // insert/erase 179 | iterator insert(const value_type& x) { 180 | return t.insert_equal(x); 181 | } 182 | iterator insert(iterator position, const value_type& x) { 183 | typedef typename rep_type::iterator rep_iterator; 184 | return t.insert_equal((rep_iterator&)position, x); 185 | } 186 | 187 | #ifdef __STL_MEMBER_TEMPLATES 188 | template 189 | void insert(InputIterator first, InputIterator last) { 190 | t.insert_equal(first, last); 191 | } 192 | #else 193 | void insert(const value_type* first, const value_type* last) { 194 | t.insert_equal(first, last); 195 | } 196 | void insert(const_iterator first, const_iterator last) { 197 | t.insert_equal(first, last); 198 | } 199 | #endif /* __STL_MEMBER_TEMPLATES */ 200 | ... 201 | }; 202 | ``` 203 | 204 | 205 | 206 | **erase** 删除操作, 同样都是以RB-tree为接口 207 | 208 | ```c++ 209 | class multiset { 210 | ... 211 | public: 212 | void erase(iterator position) { 213 | typedef typename rep_type::iterator rep_iterator; 214 | t.erase((rep_iterator&)position); 215 | } 216 | size_type erase(const key_type& x) { 217 | return t.erase(x); 218 | } 219 | void erase(iterator first, iterator last) { 220 | typedef typename rep_type::iterator rep_iterator; 221 | t.erase((rep_iterator&)first, (rep_iterator&)last); 222 | } 223 | void clear() { t.clear(); } 224 | ... 225 | }; 226 | ``` 227 | 228 | **find等函数** 229 | 230 | ```c++ 231 | class multiset { 232 | ... 233 | public: 234 | iterator find(const key_type& x) const { return t.find(x); } 235 | size_type count(const key_type& x) const { return t.count(x); } 236 | iterator lower_bound(const key_type& x) const { 237 | return t.lower_bound(x); 238 | } 239 | iterator upper_bound(const key_type& x) const { 240 | return t.upper_bound(x); 241 | } 242 | pair equal_range(const key_type& x) const { 243 | return t.equal_range(x); 244 | } 245 | }; 246 | ``` 247 | 248 | 249 | 250 | ### 总结 251 | 252 | multiset与set最大的不同就是可以重复的插入键值, 一个以`insert_equal`为接口, 一个以`insert_uniqual`为接口, 下一节我们分析map与multimap的区别 -------------------------------------------------------------------------------- /32 multimap.md: -------------------------------------------------------------------------------- 1 | # multimap 2 | 3 | ### 前言 4 | 5 | 前面我们分析了map, 知道map是不允许插入相同的键值的, 也不会保存第二次的数据, 而本节分析的`multimap`与`map`不同, 它允许多个重复的键值插入. 6 | 7 | 8 | 9 | ### mutimap操作 10 | 11 | ```c++ 12 | int main() 13 | { 14 | multimap multi; 15 | // 这里重复插入了两个相同的键值 16 | multi.insert(make_pair("one", 2)); 17 | multi.insert(make_pair("one", 1)); 18 | 19 | cout << (*multi.find("one")).second << endl; // 2 20 | 21 | // 将两个相同键值都输出, 确定相同的键值能重复插入 22 | for(const auto &i : multi) 23 | cout << i.first << " " << i.second << endl; 24 | // one 2 25 | // one 1 26 | 27 | exit(0); 28 | } 29 | ``` 30 | 31 | 32 | 33 | ### mutimap分析 34 | 35 | **定义类型** 36 | 37 | ```c++ 38 | #ifndef __STL_LIMITED_DEFAULT_TEMPLATES 39 | template , class Alloc = alloc> 40 | #else 41 | template 42 | #endif 43 | class multimap { 44 | public: 45 | // typedefs: 46 | typedef Key key_type; 47 | typedef T data_type; 48 | typedef T mapped_type; 49 | typedef pair value_type; // 同样, 键值不允许进行修改 50 | typedef Compare key_compare; 51 | private: 52 | typedef rb_tree, key_compare, Alloc> rep_type; 54 | rep_type t; // red-black tree representing multimap 55 | public: 56 | typedef typename rep_type::pointer pointer; 57 | typedef typename rep_type::const_pointer const_pointer; 58 | typedef typename rep_type::reference reference; 59 | typedef typename rep_type::const_reference const_reference; 60 | typedef typename rep_type::iterator iterator; 61 | typedef typename rep_type::const_iterator const_iterator; 62 | typedef typename rep_type::reverse_iterator reverse_iterator; 63 | typedef typename rep_type::const_reverse_iterator const_reverse_iterator; 64 | typedef typename rep_type::size_type size_type; 65 | typedef typename rep_type::difference_type difference_type; 66 | ... 67 | }; 68 | ``` 69 | 70 | 71 | 72 | **嵌套类** 实际定义的是一个仿函数. 73 | 74 | ```c++ 75 | class multimap { 76 | public: 77 | class value_compare : public binary_function { 78 | friend class multimap; 79 | protected: 80 | Compare comp; 81 | value_compare(Compare c) : comp(c) {} 82 | public: 83 | // 仿函数 84 | bool operator()(const value_type& x, const value_type& y) const { 85 | return comp(x.first, y.first); 86 | } 87 | }; 88 | ... 89 | }; 90 | ``` 91 | 92 | 93 | 94 | **构造函数** 95 | 96 | 与`map`不同, map是以RB-tree的`insert_uniqual`为接口, 而multimap是以`insert_qual`为接口, 所以允许键值重复. 97 | 98 | ```c++ 99 | class multimap { 100 | ... 101 | public: 102 | // allocation/deallocation 103 | multimap() : t(Compare()) { } 104 | explicit multimap(const Compare& comp) : t(comp) { } 105 | 106 | #ifdef __STL_MEMBER_TEMPLATES 107 | template 108 | multimap(InputIterator first, InputIterator last) 109 | : t(Compare()) { t.insert_equal(first, last); } 110 | 111 | template 112 | multimap(InputIterator first, InputIterator last, const Compare& comp) 113 | : t(comp) { t.insert_equal(first, last); } 114 | #else 115 | multimap(const value_type* first, const value_type* last) 116 | : t(Compare()) { t.insert_equal(first, last); } 117 | multimap(const value_type* first, const value_type* last, 118 | const Compare& comp) 119 | : t(comp) { t.insert_equal(first, last); } 120 | 121 | multimap(const_iterator first, const_iterator last) 122 | : t(Compare()) { t.insert_equal(first, last); } 123 | multimap(const_iterator first, const_iterator last, const Compare& comp) 124 | : t(comp) { t.insert_equal(first, last); } 125 | #endif /* __STL_MEMBER_TEMPLATES */ 126 | ... 127 | }; 128 | ``` 129 | 130 | 131 | 132 | **基本属性获取** 133 | 134 | ```c++ 135 | class multimap { 136 | ... 137 | public: 138 | key_compare key_comp() const { return t.key_comp(); } 139 | // 返回的是一个仿函数 140 | value_compare value_comp() const { return value_compare(t.key_comp()); } 141 | iterator begin() { return t.begin(); } 142 | const_iterator begin() const { return t.begin(); } 143 | iterator end() { return t.end(); } 144 | const_iterator end() const { return t.end(); } 145 | reverse_iterator rbegin() { return t.rbegin(); } 146 | const_reverse_iterator rbegin() const { return t.rbegin(); } 147 | reverse_iterator rend() { return t.rend(); } 148 | const_reverse_iterator rend() const { return t.rend(); } 149 | bool empty() const { return t.empty(); } 150 | size_type size() const { return t.size(); } 151 | size_type max_size() const { return t.max_size(); } 152 | // 交换 153 | void swap(multimap& x) { t.swap(x.t); } 154 | ... 155 | }; 156 | ``` 157 | 158 | 159 | 160 | **重载** 161 | 162 | ```c++ 163 | class multimap { 164 | ... 165 | public: 166 | multimap(const multimap& x) : t(x.t) { } 167 | multimap& 168 | operator=(const multimap& x) { 169 | t = x.t; 170 | return *this; 171 | } 172 | friend bool operator== __STL_NULL_TMPL_ARGS (const multimap&, const multimap&); 173 | friend bool operator< __STL_NULL_TMPL_ARGS (const multimap&, const multimap&); 174 | ... 175 | }; 176 | template 177 | inline bool operator==(const multimap& x, 178 | const multimap& y) { 179 | return x.t == y.t; 180 | } 181 | 182 | template 183 | inline bool operator<(const multimap& x, 184 | const multimap& y) { 185 | return x.t < y.t; 186 | } 187 | 188 | #ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER 189 | 190 | template 191 | inline void swap(multimap& x, 192 | multimap& y) { 193 | x.swap(y); 194 | } 195 | ``` 196 | 197 | 与`map`不同, multimap没有重载`[]`运算符, 毕竟multimap是允许键值重复, `[]`就无法确认具体是操作的哪一个键值. 198 | 199 | 200 | 201 | **find, insert, erase** 202 | 203 | find等成员函数都是调用RB-tree的接口实现. 这里也就不具体分析. 204 | 205 | ```c++ 206 | class multimap { 207 | ... 208 | public: 209 | // insert/erase 210 | // 其插入方式与map一样, 都是接受pair结构和迭代器 211 | iterator insert(const value_type& x) { return t.insert_equal(x); } 212 | iterator insert(iterator position, const value_type& x) { 213 | return t.insert_equal(position, x); 214 | } 215 | #ifdef __STL_MEMBER_TEMPLATES 216 | template 217 | void insert(InputIterator first, InputIterator last) { 218 | t.insert_equal(first, last); 219 | } 220 | #else 221 | void insert(const value_type* first, const value_type* last) { 222 | t.insert_equal(first, last); 223 | } 224 | void insert(const_iterator first, const_iterator last) { 225 | t.insert_equal(first, last); 226 | } 227 | #endif /* __STL_MEMBER_TEMPLATES */ 228 | void erase(iterator position) { t.erase(position); } 229 | size_type erase(const key_type& x) { return t.erase(x); } 230 | void erase(iterator first, iterator last) { t.erase(first, last); } 231 | void clear() { t.clear(); } 232 | // multimap operations: 233 | iterator find(const key_type& x) { return t.find(x); } 234 | const_iterator find(const key_type& x) const { return t.find(x); } 235 | size_type count(const key_type& x) const { return t.count(x); } 236 | iterator lower_bound(const key_type& x) {return t.lower_bound(x); } 237 | const_iterator lower_bound(const key_type& x) const { 238 | return t.lower_bound(x); 239 | } 240 | iterator upper_bound(const key_type& x) {return t.upper_bound(x); } 241 | const_iterator upper_bound(const key_type& x) const { 242 | return t.upper_bound(x); 243 | } 244 | pair equal_range(const key_type& x) { 245 | return t.equal_range(x); 246 | } 247 | pair equal_range(const key_type& x) const { 248 | return t.equal_range(x); 249 | } 250 | }; 251 | ``` 252 | 253 | 254 | 255 | ### 总结 256 | 257 | multimap与map 两者都是配接器, 大多数操作都是相同的, 不同之处 258 | 259 | 1. **前者调用RB-tree的`insert_equal`所以允许键值重复插入** 260 | 2. **前者没有重载`[]`, 因为有多个重复的键值, 无法具体确认哪一个值. ** 261 | 262 | 下一节我们继续分析其他的关联容器-`hashtable` -------------------------------------------------------------------------------- /33 hashtable 上.md: -------------------------------------------------------------------------------- 1 | # hashtable 2 | 3 | ### 前言 4 | 5 | 前面我们分析过RB-tree关联容器, `RB-tree`在插入(可重复和不可重复), 删除等操作时间复杂度都是O(nlngn), 以及满足5个规则, 以及以他为底层的配接器; 本节就来分析`hashtable`另个关联容器, 他在插入, 删除等操作都可以做到O(1)的时间复杂度. 6 | 7 | 8 | 9 | ### 哈希表概念 10 | 11 | #### 哈希方法 12 | 13 | 1. **直接定址法**:取关键字或关键字的某个线性函数值为散列地址。(这种散列函数叫做自身函数) 14 | 2. **数字分析法**:假设关键字是以r为基的数,并且哈希表中可能出现的关键字都是事先知道的,则可取关键字的若干数位组成哈希地址。 15 | 3. **平方取中法**:取关键字平方后的中间几位为哈希地址。通常在选定哈希函数时不一定能知道关键字的全部情况,取其中的哪几位也不一定合适,而一个数平方后的中间几位数和数的每一位都相关,由此使随机分布的关键字得到的哈希地址也是随机的。取的位数由表长决定。 16 | 4. **折叠法**:将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为哈希地址。 17 | 5. **随机数法** 18 | 6.**除留余数法**:取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址。不仅可以对关键字直接取模,也可在折叠法、平方取中法等运算之后取模。对p的选择很重要,一般取素数或m,若p选择不好,容易产生冲突。 19 | 20 | *hashtable解决冲突的办法就是开链.* 21 | 22 | 23 | 24 | #### 冲突处理 25 | 26 | 哈希表的冲突处理也有很多种. 27 | 28 | 1. 开放定址法 29 | - 线性探测 : 本来的位置被占有(冲突), 重新再往后找到第一个有空的位置插入进去 30 | - 二次探测 : 本来的位置被占有(冲突), 每次有冲突就平方一次重新查找 31 | 2. 开链 : 本来的位置被占有(冲突), 形成一个链表插入到链表中 32 | 33 | **装载因子 : 装入表中的元素 / 表的实际大小.** 装载因子越大说明冲突的可能性就越大. 34 | 35 | 36 | 37 | ### hashtable分析 38 | 39 | #### 桶与节点. 40 | 41 | 桶 : 定义的哈希表大小, 以vector为桶 42 | 43 | 节点 : 链表 44 | 45 | ```c++ 46 | // 这里链表是自定义的, 并没有采用list和slist 47 | template 48 | struct __hashtable_node 49 | { 50 | __hashtable_node* next; 51 | Value val; 52 | }; 53 | ``` 54 | 55 | 56 | 57 | ```c++ 58 | // 前置声明 59 | template 61 | class hashtable; 62 | 63 | template 65 | struct __hashtable_iterator; 66 | 67 | template 69 | struct __hashtable_const_iterator; 70 | ``` 71 | 72 | 73 | 74 | #### hashtable迭代器 75 | 76 | hashtable迭代器是`forward_iterator_tag`类型, 正向迭代器, 所以他也就没有重载`--` , 没有回退. 77 | 78 | `__hashtable_const_iterator`与`__hashtable_iterator`一样, 这里就只分析后者 79 | 80 | ```c++ 81 | template 82 | struct __hashtable_iterator { 83 | typedef hashtable hashtable; 84 | typedef __hashtable_iterator 85 | iterator; 86 | typedef __hashtable_const_iterator 87 | const_iterator; 88 | typedef __hashtable_node node; 89 | 90 | typedef forward_iterator_tag iterator_category; // 正向迭代器 91 | typedef Value value_type; 92 | typedef ptrdiff_t difference_type; 93 | typedef size_t size_type; 94 | typedef Value& reference; 95 | typedef Value* pointer; 96 | 97 | node* cur; // 定义节点 98 | hashtable* ht; // 定义哈希表指针 99 | 100 | __hashtable_iterator(node* n, hashtable* tab) : cur(n), ht(tab) {} 101 | __hashtable_iterator() {} 102 | // 重载指针 103 | reference operator*() const { return cur->val; } 104 | #ifndef __SGI_STL_NO_ARROW_OPERATOR 105 | pointer operator->() const { return &(operator*()); } 106 | #endif /* __SGI_STL_NO_ARROW_OPERATOR */ 107 | // 重在++, 因为是正向迭代器, 所以没有-- 108 | iterator& operator++(); 109 | iterator operator++(int); 110 | bool operator==(const iterator& it) const { return cur == it.cur; } 111 | bool operator!=(const iterator& it) const { return cur != it.cur; } 112 | }; 113 | ``` 114 | 115 | 116 | 117 | #### 定义哈希表大小 118 | 119 | 定义了哈希表的大小, 默认long为32位, 定义了28个数组大小. 哈希表的的大小都是素数, 减少冲突 120 | 121 | ```c++ 122 | // Note: assumes long is at least 32 bits. 123 | static const int __stl_num_primes = 28; 124 | static const unsigned long __stl_prime_list[__stl_num_primes] = 125 | { 126 | 53, 97, 193, 389, 769, 127 | 1543, 3079, 6151, 12289, 24593, 128 | 49157, 98317, 196613, 393241, 786433, 129 | 1572869, 3145739, 6291469, 12582917, 25165843, 130 | 50331653, 100663319, 201326611, 402653189, 805306457, 131 | 1610612741, 3221225473, 4294967291 132 | }; 133 | // 找到大于n最近的素数 134 | inline unsigned long __stl_next_prime(unsigned long n) 135 | { 136 | const unsigned long* first = __stl_prime_list; 137 | const unsigned long* last = __stl_prime_list + __stl_num_primes; 138 | const unsigned long* pos = lower_bound(first, last, n); 139 | return pos == last ? *(last - 1) : *pos; 140 | } 141 | ``` 142 | 143 | 144 | 145 | #### 哈希表 146 | 147 | **hashtable类型定义** 148 | 149 | 模板参数含义 : 150 | 151 | 1. Value : 节点的实值类型 152 | 2. Key : 节点的键值类型 153 | 3. HashFcn : hash function的类型 154 | 4. ExtractKey : 从节点中取出键值的方法(函数或仿函数) 155 | 5. EqualKey : 判断键值是否相同的方法(函数或仿函数) 156 | 157 | ```c++ 158 | template 159 | class hashtable { 160 | public: 161 | typedef Key key_type; 162 | typedef Value value_type; 163 | typedef HashFcn hasher; 164 | typedef EqualKey key_equal; 165 | 166 | typedef size_t size_type; 167 | typedef ptrdiff_t difference_type; 168 | typedef value_type* pointer; 169 | typedef const value_type* const_pointer; 170 | typedef value_type& reference; 171 | typedef const value_type& const_reference; 172 | 173 | // 这里返回的都是仿函数 174 | hasher hash_funct() const { return hash; } 175 | key_equal key_eq() const { return equals; } 176 | 177 | private: 178 | // 这里定义的都是函数或者仿函数 179 | hasher hash; 180 | key_equal equals; 181 | ExtractKey get_key; 182 | 183 | typedef __hashtable_node node; 184 | typedef simple_alloc node_allocator; 185 | 186 | vector buckets; // 以vector作为桶, node* 187 | size_type num_elements; // 哈希表中元素个数的计数 188 | 189 | public: 190 | typedef __hashtable_iterator iterator; 191 | 192 | typedef __hashtable_const_iterator const_iterator; 193 | 194 | // 迭代器定义为友元 195 | friend struct 196 | __hashtable_iterator; 197 | friend struct 198 | __hashtable_const_iterator; 199 | ... 200 | }; 201 | ``` 202 | 203 | 204 | 205 | **构造与析构函数** 206 | 207 | ```c++ 208 | template 209 | class hashtable { 210 | ... 211 | public: 212 | // 构造函数, 没有定义默认构造函数 213 | hashtable(size_type n, const HashFcn& hf,const EqualKey& eql,const ExtractKey& ext) 214 | : hash(hf), equals(eql), get_key(ext), num_elements(0) 215 | { 216 | initialize_buckets(n); 217 | } 218 | 219 | hashtable(size_type n, const HashFcn& hf, const EqualKey& eql) 220 | : hash(hf), equals(eql), get_key(ExtractKey()), num_elements(0) 221 | { 222 | initialize_buckets(n); 223 | } 224 | // 拷贝构造函数 225 | hashtable(const hashtable& ht) 226 | : hash(ht.hash), equals(ht.equals), get_key(ht.get_key), num_elements(0) 227 | { 228 | copy_from(ht); 229 | } 230 | // 析构函数 231 | ~hashtable() { clear(); } 232 | ... 233 | }; 234 | ``` 235 | 236 | 237 | 238 | **基本属性获取** 239 | 240 | ```c++ 241 | template 242 | class hashtable { 243 | ... 244 | public: 245 | size_type size() const { return num_elements; } 246 | size_type max_size() const { return size_type(-1); } 247 | bool empty() const { return size() == 0; } 248 | 249 | // 交换, 并不是交换所有数据, 只是交换了其指针指向和个数 250 | void swap(hashtable& ht) 251 | { 252 | __STD::swap(hash, ht.hash); 253 | __STD::swap(equals, ht.equals); 254 | __STD::swap(get_key, ht.get_key); 255 | buckets.swap(ht.buckets); 256 | __STD::swap(num_elements, ht.num_elements); 257 | } 258 | 259 | iterator begin() 260 | { 261 | for (size_type n = 0; n < buckets.size(); ++n) 262 | // 从头遍历桶, 如果有不空的链表存在, 就返回该链表的第一个元素 263 | if (buckets[n]) 264 | return iterator(buckets[n], this); 265 | // 没有元素就返回end. 266 | return end(); 267 | } 268 | // end返回0 269 | iterator end() { return iterator(0, this); } 270 | 271 | const_iterator begin() const 272 | { 273 | for (size_type n = 0; n < buckets.size(); ++n) 274 | if (buckets[n]) 275 | return const_iterator(buckets[n], this); 276 | return end(); 277 | } 278 | const_iterator end() const { return const_iterator(0, this); } 279 | 280 | // 返回桶的大小 281 | size_type bucket_count() const { return buckets.size(); } 282 | 283 | size_type max_bucket_count() const 284 | { return __stl_prime_list[__stl_num_primes - 1]; } 285 | 286 | // 返回指定位置的节点的个数 287 | size_type elems_in_bucket(size_type bucket) const 288 | { 289 | size_type result = 0; 290 | for (node* cur = buckets[bucket]; cur; cur = cur->next) 291 | result += 1; 292 | return result; 293 | } 294 | ... 295 | }; 296 | ``` 297 | 298 | 299 | 300 | ### 总结 301 | 302 | 本节只是分析了哈希表的基本构成是桶(vector), 链表(解决冲突). `hashtable`是`forward_iterator_tag`类型的正向迭代器,没有`--`操作, 下节我们继续分析剩下的代码. 303 | 304 | -------------------------------------------------------------------------------- /35 hash_set.md: -------------------------------------------------------------------------------- 1 | # hash_set 2 | 3 | ### 前言 4 | 5 | 前面我们分析了hashtable, 后面的几节我们来分析其衍生出来的配接器, 本节分析的`hash_set`, 他与`set`最大的不同在与前者的元素是无序排列的, 后者是有序排列 6 | 7 | 8 | 9 | ### hash_set操作 10 | 11 | ```c++ 12 | int main() 13 | { 14 | __gnu_cxx::hash_set h_set(1); 15 | h_set.insert("one"); 16 | h_set.insert("two"); 17 | cout << "h_set size = " << h_set.size() << endl; // h_set size = 2 18 | 19 | __gnu_cxx::hash_set set_h(h_set.begin(), h_set.end()); 20 | // 这里不相等的原因两者的桶并不是一样大, 所以数据存放也不同 21 | if(h_set == set_h) 22 | h_set.clear(); 23 | cout << "h_set size = " << h_set.size() << endl; // h_set size = 2 24 | cout << "set_h size = " << set_h.size() << endl; // set_h size = 2 25 | 26 | cout << (*set_h.find("one")) << endl; // one 27 | for (const auto &i : set_h) 28 | cout << i << " "; // two one 29 | 30 | exit(0); 31 | } 32 | ``` 33 | 34 | 35 | 36 | ### hash_set分析 37 | 38 | hash_set将哈希表的接口在进行了一次封装, 实现与set类似的功能. 39 | 40 | #### 基本类型 41 | 42 | ```c++ 43 | #ifndef __STL_LIMITED_DEFAULT_TEMPLATES 44 | template , 45 | class EqualKey = equal_to, 46 | class Alloc = alloc> 47 | #else 48 | template 49 | #endif 50 | class hash_set 51 | { 52 | private: 53 | // 定义hashtable 54 | typedef hashtable, EqualKey, Alloc> ht; 55 | ht rep; 56 | 57 | public: 58 | typedef typename ht::key_type key_type; 59 | typedef typename ht::value_type value_type; 60 | typedef typename ht::hasher hasher; 61 | typedef typename ht::key_equal key_equal; 62 | 63 | // 定义为const类型, 键值不允许修改 64 | typedef typename ht::size_type size_type; 65 | typedef typename ht::difference_type difference_type; 66 | typedef typename ht::const_pointer pointer; 67 | typedef typename ht::const_pointer const_pointer; 68 | typedef typename ht::const_reference reference; 69 | typedef typename ht::const_reference const_reference; 70 | 71 | // 定义迭代器 72 | typedef typename ht::const_iterator iterator; 73 | typedef typename ht::const_iterator const_iterator; 74 | // 仿函数 75 | hasher hash_funct() const { return rep.hash_funct(); } 76 | key_equal key_eq() const { return rep.key_eq(); } 77 | ... 78 | }; 79 | ``` 80 | 81 | 82 | 83 | #### 构造函数 84 | 85 | ```c++ 86 | class hash_set 87 | { 88 | ... 89 | public: 90 | hash_set() : rep(100, hasher(), key_equal()) {} // 默认构造函数, 表大小默认为100最近的素数 91 | explicit hash_set(size_type n) : rep(n, hasher(), key_equal()) {} 92 | hash_set(size_type n, const hasher& hf) : rep(n, hf, key_equal()) {} 93 | hash_set(size_type n, const hasher& hf, const key_equal& eql) 94 | : rep(n, hf, eql) {} 95 | 96 | #ifdef __STL_MEMBER_TEMPLATES 97 | template 98 | hash_set(InputIterator f, InputIterator l) 99 | : rep(100, hasher(), key_equal()) { rep.insert_unique(f, l); } 100 | template 101 | hash_set(InputIterator f, InputIterator l, size_type n) 102 | : rep(n, hasher(), key_equal()) { rep.insert_unique(f, l); } 103 | template 104 | hash_set(InputIterator f, InputIterator l, size_type n, 105 | const hasher& hf) 106 | : rep(n, hf, key_equal()) { rep.insert_unique(f, l); } 107 | template 108 | hash_set(InputIterator f, InputIterator l, size_type n, 109 | const hasher& hf, const key_equal& eql) 110 | : rep(n, hf, eql) { rep.insert_unique(f, l); } 111 | ... 112 | }; 113 | ``` 114 | 115 | 116 | 117 | #### 功能实现 118 | 119 | ```c++ 120 | class hash_set 121 | { 122 | ... 123 | public: 124 | // 都是调用hashtable的接口 125 | size_type size() const { return rep.size(); } // 哈希表的元素个数 126 | size_type max_size() const { return rep.max_size(); } 127 | bool empty() const { return rep.empty(); } 128 | void swap(hash_set& hs) { rep.swap(hs.rep); } 129 | friend bool operator== __STL_NULL_TMPL_ARGS (const hash_set&, 130 | const hash_set&); 131 | 132 | iterator begin() const { return rep.begin(); } 133 | iterator end() const { return rep.end(); } 134 | ... 135 | }; 136 | ``` 137 | 138 | **插入删除等操作** 139 | 140 | insert调用的是`insert_unqiue`函数 141 | 142 | ```c++ 143 | class hash_set 144 | { 145 | ... 146 | public: 147 | // 都是调用hashtable的接口, 这里insert_unqiue函数 148 | pair insert(const value_type& obj) 149 | { 150 | pair p = rep.insert_unique(obj); 151 | return pair(p.first, p.second); 152 | } 153 | #ifdef __STL_MEMBER_TEMPLATES 154 | template 155 | void insert(InputIterator f, InputIterator l) { rep.insert_unique(f,l); } 156 | #else 157 | void insert(const value_type* f, const value_type* l) { 158 | rep.insert_unique(f,l); 159 | } 160 | void insert(const_iterator f, const_iterator l) {rep.insert_unique(f, l); } 161 | #endif /*__STL_MEMBER_TEMPLATES */ 162 | pair insert_noresize(const value_type& obj) 163 | { 164 | pair p = rep.insert_unique_noresize(obj); 165 | return pair(p.first, p.second); 166 | } 167 | 168 | // 查找 169 | iterator find(const key_type& key) const { return rep.find(key); } 170 | 171 | // 计数 172 | size_type count(const key_type& key) const { return rep.count(key); } 173 | 174 | pair equal_range(const key_type& key) const 175 | { return rep.equal_range(key); } 176 | 177 | // 删除 178 | size_type erase(const key_type& key) {return rep.erase(key); } 179 | void erase(iterator it) { rep.erase(it); } 180 | void erase(iterator f, iterator l) { rep.erase(f, l); } 181 | void clear() { rep.clear(); } 182 | 183 | public: 184 | void resize(size_type hint) { rep.resize(hint); } 185 | size_type bucket_count() const { return rep.bucket_count(); } 186 | size_type max_bucket_count() const { return rep.max_bucket_count(); } 187 | size_type elems_in_bucket(size_type n) const 188 | { return rep.elems_in_bucket(n); } 189 | }; 190 | ``` 191 | 192 | **重载** 193 | 194 | ```c++ 195 | template 196 | inline bool operator==(const hash_set& hs1, 197 | const hash_set& hs2) 198 | { 199 | return hs1.rep == hs2.rep; 200 | } 201 | 202 | template 203 | inline void swap(hash_set& hs1, 204 | hash_set& hs2) { 205 | hs1.swap(hs2); 206 | } 207 | ``` 208 | 209 | 210 | 211 | ### 总结 212 | 213 | 本节也只是简单对hash_set做了分析, 因为所有的操作都是通过调用`hashtable`的接口实现的, 而且也与`set`的功能类似, 最大的不同就是两者的有序和无序. 下一节我们分析hash_multiset, 这也是跟multiset类似的功能. 214 | 215 | -------------------------------------------------------------------------------- /36 hash_multiset.md: -------------------------------------------------------------------------------- 1 | # hash_multiset 2 | 3 | ### 前言 4 | 5 | 上节分析了hash_set, 他与set有很多的相似之处, 键不能重复, 不能修改等, 本节分析的hash_multiset与multiset又有很多相似之处, 键值可以重复. 6 | 7 | 8 | 9 | ### hash_multiset分析 10 | 11 | 同样也是以hashtable为底层的配接器. 12 | 13 | **定义数据类型** 14 | 15 | ```c++ 16 | #ifndef __STL_LIMITED_DEFAULT_TEMPLATES 17 | template , 18 | class EqualKey = equal_to, 19 | class Alloc = alloc> 20 | #else 21 | template 22 | #endif 23 | class hash_multiset 24 | { 25 | private: 26 | // 定义红黑树 27 | typedef hashtable, 28 | EqualKey, Alloc> ht; 29 | ht rep; 30 | 31 | public: 32 | // 定义基本类型 33 | typedef typename ht::key_type key_type; 34 | typedef typename ht::value_type value_type; 35 | typedef typename ht::hasher hasher; 36 | typedef typename ht::key_equal key_equal; 37 | 38 | // 定义的大都是const类型, 键不允许进行修改 39 | typedef typename ht::size_type size_type; 40 | typedef typename ht::difference_type difference_type; 41 | typedef typename ht::const_pointer pointer; 42 | typedef typename ht::const_pointer const_pointer; 43 | typedef typename ht::const_reference reference; 44 | typedef typename ht::const_reference const_reference; 45 | 46 | // 迭代器也是const类型 47 | typedef typename ht::const_iterator iterator; 48 | typedef typename ht::const_iterator const_iterator; 49 | 50 | // 返回的是仿函数 51 | hasher hash_funct() const { return rep.hash_funct(); } 52 | key_equal key_eq() const { return rep.key_eq(); } 53 | ... 54 | }; 55 | ``` 56 | 57 | 58 | 59 | **构造函数** 60 | 61 | ```c++ 62 | class hash_multiset 63 | { 64 | ... 65 | public: 66 | hash_multiset() : rep(100, hasher(), key_equal()) {} // 默认构造函数, 表大小默认为100最近的素数 67 | explicit hash_multiset(size_type n) : rep(n, hasher(), key_equal()) {} 68 | hash_multiset(size_type n, const hasher& hf) : rep(n, hf, key_equal()) {} 69 | hash_multiset(size_type n, const hasher& hf, const key_equal& eql) 70 | : rep(n, hf, eql) {} 71 | 72 | // 接受迭代器 73 | template 74 | hash_multiset(InputIterator f, InputIterator l) 75 | : rep(100, hasher(), key_equal()) { rep.insert_equal(f, l); } 76 | template 77 | hash_multiset(InputIterator f, InputIterator l, size_type n) 78 | : rep(n, hasher(), key_equal()) { rep.insert_equal(f, l); } 79 | template 80 | hash_multiset(InputIterator f, InputIterator l, size_type n, 81 | const hasher& hf) 82 | : rep(n, hf, key_equal()) { rep.insert_equal(f, l); } 83 | template 84 | hash_multiset(InputIterator f, InputIterator l, size_type n, 85 | const hasher& hf, const key_equal& eql) 86 | : rep(n, hf, eql) { rep.insert_equal(f, l); } 87 | ... 88 | }; 89 | ``` 90 | 91 | 92 | 93 | **功能实现** 94 | 95 | ```c++ 96 | class hash_multiset 97 | { 98 | ... 99 | public: 100 | size_type size() const { return rep.size(); } // 表中的元素个数 101 | size_type max_size() const { return rep.max_size(); } 102 | bool empty() const { return rep.empty(); } 103 | void swap(hash_multiset& hs) { rep.swap(hs.rep); } // 交换 104 | // 重载 105 | friend bool operator== __STL_NULL_TMPL_ARGS (const hash_multiset&, 106 | const hash_multiset&); 107 | 108 | iterator begin() const { return rep.begin(); } 109 | iterator end() const { return rep.end(); } 110 | 111 | iterator find(const key_type& key) const { return rep.find(key); } 112 | 113 | size_type count(const key_type& key) const { return rep.count(key); } 114 | public: 115 | void resize(size_type hint) { rep.resize(hint); } // 重新定义表的大小 116 | size_type bucket_count() const { return rep.bucket_count(); } 117 | size_type max_bucket_count() const { return rep.max_bucket_count(); } 118 | size_type elems_in_bucket(size_type n) const // 链表中元素的个数 119 | { return rep.elems_in_bucket(n); } 120 | 121 | // 删除操作也是调用的是其接口 122 | size_type erase(const key_type& key) {return rep.erase(key); } 123 | void erase(iterator it) { rep.erase(it); } 124 | void erase(iterator f, iterator l) { rep.erase(f, l); } 125 | void clear() { rep.clear(); } 126 | ... 127 | }; 128 | template 129 | inline bool operator==(const hash_multiset& hs1, 130 | const hash_multiset& hs2) 131 | { 132 | return hs1.rep == hs2.rep; 133 | } 134 | ``` 135 | 136 | 137 | 138 | **insert** 调用的是`insert_equal`接口 139 | 140 | ```c++ 141 | class hash_multiset 142 | { 143 | ... 144 | public: 145 | iterator insert(const value_type& obj) { return rep.insert_equal(obj); } 146 | #ifdef __STL_MEMBER_TEMPLATES 147 | template 148 | void insert(InputIterator f, InputIterator l) { rep.insert_equal(f,l); } 149 | #else 150 | void insert(const value_type* f, const value_type* l) { 151 | rep.insert_equal(f,l); 152 | } 153 | void insert(const_iterator f, const_iterator l) { rep.insert_equal(f, l); } 154 | #endif /*__STL_MEMBER_TEMPLATES */ 155 | iterator insert_noresize(const value_type& obj) 156 | { return rep.insert_equal_noresize(obj); } 157 | 158 | pair equal_range(const key_type& key) const 159 | { return rep.equal_range(key); } 160 | ... 161 | }; 162 | ``` 163 | 164 | 165 | 166 | ### 总结 167 | 168 | 本节分析了`hash_multiset`, 它与`hash_set`不一样之处就在于他可以支持重复插入键值, 而后者则不行. 下面我们继续分析hash实现的map, 同样与RB-tree实现的功能基本一样. 169 | 170 | -------------------------------------------------------------------------------- /37 hash_map.md: -------------------------------------------------------------------------------- 1 | # hash_map 2 | 3 | ### 前言 4 | 5 | hash_map是以hashtable为底层的配接器, 他与map的功能基本一样, 只是map是有序的将键值插入, 而`hash_map`则是无序的插入键值, 本节也简单对它做一个分析. 6 | 7 | 8 | 9 | ### hash_map例子 10 | 11 | ```c++ 12 | 13 | ``` 14 | 15 | 16 | 17 | ### hash_map分析 18 | 19 | **类型定义** 20 | 21 | ```c++ 22 | #ifndef __STL_LIMITED_DEFAULT_TEMPLATES 23 | template , 24 | class EqualKey = equal_to, 25 | class Alloc = alloc> 26 | #else 27 | template 29 | #endif 30 | class hash_map 31 | { 32 | private: 33 | // 定义hashtable 34 | typedef hashtable, Key, HashFcn, 35 | select1st >, EqualKey, Alloc> ht; 36 | ht rep; 37 | 38 | public: 39 | // 类型定义 40 | typedef typename ht::key_type key_type; 41 | typedef T data_type; 42 | typedef T mapped_type; 43 | typedef typename ht::value_type value_type; 44 | typedef typename ht::hasher hasher; 45 | typedef typename ht::key_equal key_equal; 46 | 47 | typedef typename ht::size_type size_type; 48 | typedef typename ht::difference_type difference_type; 49 | typedef typename ht::pointer pointer; 50 | typedef typename ht::const_pointer const_pointer; 51 | typedef typename ht::reference reference; 52 | typedef typename ht::const_reference const_reference; 53 | 54 | typedef typename ht::iterator iterator; 55 | typedef typename ht::const_iterator const_iterator; 56 | // 返回值是仿函数 57 | hasher hash_funct() const { return rep.hash_funct(); } 58 | key_equal key_eq() const { return rep.key_eq(); } 59 | ... 60 | }; 61 | ``` 62 | 63 | 64 | 65 | **构造函数** 66 | 67 | ```c++ 68 | class hash_map 69 | { 70 | ... 71 | public: 72 | // 构造函数 73 | hash_map() : rep(100, hasher(), key_equal()) {} // 默认构造函数 74 | explicit hash_map(size_type n) : rep(n, hasher(), key_equal()) {} 75 | hash_map(size_type n, const hasher& hf) : rep(n, hf, key_equal()) {} 76 | hash_map(size_type n, const hasher& hf, const key_equal& eql) 77 | : rep(n, hf, eql) {} 78 | // 接受迭代器 79 | template 80 | hash_map(InputIterator f, InputIterator l) 81 | : rep(100, hasher(), key_equal()) { rep.insert_unique(f, l); } 82 | template 83 | hash_map(InputIterator f, InputIterator l, size_type n) 84 | : rep(n, hasher(), key_equal()) { rep.insert_unique(f, l); } 85 | template 86 | hash_map(InputIterator f, InputIterator l, size_type n, 87 | const hasher& hf) 88 | : rep(n, hf, key_equal()) { rep.insert_unique(f, l); } 89 | template 90 | hash_map(InputIterator f, InputIterator l, size_type n, 91 | const hasher& hf, const key_equal& eql) 92 | : rep(n, hf, eql) { rep.insert_unique(f, l); } 93 | ... 94 | }; 95 | ``` 96 | 97 | 98 | 99 | ```c++ 100 | class hash_map 101 | { 102 | ... 103 | public: 104 | size_type size() const { return rep.size(); } // map中的元素个数 105 | size_type max_size() const { return rep.max_size(); } 106 | bool empty() const { return rep.empty(); } 107 | void swap(hash_map& hs) { rep.swap(hs.rep); } 108 | 109 | // 获取迭代器 110 | iterator begin() { return rep.begin(); } 111 | iterator end() { return rep.end(); } 112 | const_iterator begin() const { return rep.begin(); } 113 | const_iterator end() const { return rep.end(); } 114 | iterator find(const key_type& key) { return rep.find(key); } 115 | const_iterator find(const key_type& key) const { return rep.find(key); } 116 | size_type count(const key_type& key) const { return rep.count(key); } // 获取一个元素出现的个数, map中count=0可以表示不存在 117 | ... 118 | }; 119 | ``` 120 | 121 | 122 | 123 | **重载** 124 | 125 | ```c++ 126 | class hash_map 127 | { 128 | ... 129 | public: 130 | friend bool operator== __STL_NULL_TMPL_ARGS (const hash_map&, const hash_map&); 131 | 132 | T& operator[](const key_type& key) { 133 | return rep.find_or_insert(value_type(key, T())).second; 134 | } 135 | ... 136 | }; 137 | template 138 | inline bool operator==(const hash_map& hm1, 139 | const hash_map& hm2) 140 | { 141 | return hm1.rep == hm2.rep; 142 | } 143 | ``` 144 | 145 | 146 | 147 | **insert等函数** 148 | 149 | `insert_unique`不重复的插入元素 150 | 151 | ```c++ 152 | class hash_map 153 | { 154 | ... 155 | public: 156 | // 不重复的插入元素 157 | pair insert(const value_type& obj) 158 | { return rep.insert_unique(obj); } 159 | #ifdef __STL_MEMBER_TEMPLATES 160 | // 接受迭代器 161 | template 162 | void insert(InputIterator f, InputIterator l) { rep.insert_unique(f,l); } 163 | #else 164 | void insert(const value_type* f, const value_type* l) { 165 | rep.insert_unique(f,l); 166 | } 167 | void insert(const_iterator f, const_iterator l) { rep.insert_unique(f, l); } 168 | #endif /*__STL_MEMBER_TEMPLATES */ 169 | pair insert_noresize(const value_type& obj) 170 | { return rep.insert_unique_noresize(obj); } 171 | 172 | pair equal_range(const key_type& key) 173 | { return rep.equal_range(key); } 174 | pair equal_range(const key_type& key) const 175 | { return rep.equal_range(key); } 176 | 177 | // 删除元素 178 | size_type erase(const key_type& key) {return rep.erase(key); } 179 | void erase(iterator it) { rep.erase(it); } 180 | void erase(iterator f, iterator l) { rep.erase(f, l); } 181 | void clear() { rep.clear(); } 182 | 183 | public: 184 | void resize(size_type hint) { rep.resize(hint); } 185 | size_type bucket_count() const { return rep.bucket_count(); } 186 | size_type max_bucket_count() const { return rep.max_bucket_count(); } 187 | size_type elems_in_bucket(size_type n) const 188 | { return rep.elems_in_bucket(n); } 189 | }; 190 | ``` 191 | 192 | 193 | 194 | ### 总结 195 | 196 | 本节对hash_map做了简单的分析, 基本上的分析都和前面的set, map等一样, 都是调用接口并进行封装就成了一个新的结构. `hash_map`也是不能重复的插入相同的键, 并且不能修改键. 与`map`区别在于它插入和删除的时间复杂度是O(1), 并且插入是无序的. 197 | 198 | 199 | 200 | -------------------------------------------------------------------------------- /38 hash_multimap.md: -------------------------------------------------------------------------------- 1 | # hash_multimap 2 | 3 | ### 前言 4 | 5 | 上节分析了`hash_map`知道它是不允许存在相同的键存在, 本节的`hash_multimap`就可以允许存在多个相同的键, 以`hashtable`为接口, 这里我就只分析与hash_map的不同点吧, 毕竟很多都是一样的了, 没有必要重复的分析相同的代码. 6 | 7 | 8 | 9 | ### insert 10 | 11 | 最大的区别就在这里, hash_multimap 是以`insert_equal`为接口, 所以支持插入重复的键. 12 | 13 | ```c++ 14 | #ifndef __STL_LIMITED_DEFAULT_TEMPLATES 15 | template , 16 | class EqualKey = equal_to, 17 | class Alloc = alloc> 18 | #else 19 | template 21 | #endif 22 | class hash_multimap 23 | { 24 | public: 25 | ... 26 | iterator insert(const value_type& obj) { return rep.insert_equal(obj); } 27 | #ifdef __STL_MEMBER_TEMPLATES 28 | template 29 | void insert(InputIterator f, InputIterator l) { rep.insert_equal(f,l); } 30 | #else 31 | void insert(const value_type* f, const value_type* l) { 32 | rep.insert_equal(f,l); 33 | } 34 | void insert(const_iterator f, const_iterator l) { rep.insert_equal(f, l); } 35 | #endif /*__STL_MEMBER_TEMPLATES */ 36 | iterator insert_noresize(const value_type& obj) 37 | { return rep.insert_equal_noresize(obj); } 38 | ... 39 | }; 40 | ``` 41 | 42 | 43 | 44 | ### 总结 45 | 46 | 本节很快的分析了`hash_multimap` , 主要实现区别在`insert`接口不同, 其他的功能都与hash_map一样. 从下一节我们就开始分析算法, 当然我只是选择了几个比较有意思的进行分析, 毕竟很多都直接就能理解. -------------------------------------------------------------------------------- /39 算法--copy.md: -------------------------------------------------------------------------------- 1 | # 算法--copy 2 | 3 | ### 前言 4 | 5 | 在前面分析顺序容器和关联容器时, 总会遇到`copy`这个函数, 当时并没有去分析这个函数, 毕竟都能知道他是什么功能, 本节就来揭开它面纱. 6 | 7 | 8 | 9 | ### copy分析 10 | 11 | copy函数源码在`stl_algobase.h`中, 该结构中还有很多其他的算法实现, 我只是从中挑选出了copy, 有兴趣可以自己看看里面的其他算法. 12 | 13 | `copy`同`traits`编程, 都对性能做了最优的优化, 能使用的`memmove`函数就使用它, 不能通过迭代器类型再来选择最优处理. 14 | 15 | **在偏特化与全特化中分析过, 最适合的函数会优先调用, 普通函数优先级大于模板函数** 16 | 17 | ```c++ 18 | template 19 | inline OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result) 20 | { 21 | return __copy_dispatch()(first, last, result); 22 | } 23 | // 重载 24 | // 在偏特化与全特化中分析过, 最适合的函数会优先调用, 普通函数优于模板函数 25 | inline char* copy(const char* first, const char* last, char* result) { 26 | // 直接调用memmove效率最高 27 | memmove(result, first, last - first); 28 | return result + (last - first); 29 | } 30 | inline wchar_t* copy(const wchar_t* first, const wchar_t* last, wchar_t* result) { 31 | // 直接调用memmove效率最高 32 | memmove(result, first, sizeof(wchar_t) * (last - first)); 33 | return result + (last - first); 34 | } 35 | ``` 36 | 37 | **__copy_dispatch** 通过传入参数的迭代器类型再进行优化处理 38 | 39 | ```c++ 40 | template 41 | struct __copy_dispatch 42 | { 43 | OutputIterator operator()(InputIterator first, InputIterator last, 44 | OutputIterator result) { 45 | // iterator_category获取迭代器类型, 不同迭代器选择不同的重载函数 46 | return __copy(first, last, result, iterator_category(first)); 47 | } 48 | }; 49 | ``` 50 | 51 | 52 | 53 | **输入迭代器类型处理 input_iterator_tag** 54 | 55 | ```c++ 56 | template 57 | inline OutputIterator __copy(InputIterator first, InputIterator last, 58 | OutputIterator result, input_iterator_tag) 59 | { 60 | // 通过迭代器将一个元素一个元素的复制 61 | for ( ; first != last; ++result, ++first) 62 | *result = *first; 63 | return result; 64 | } 65 | ``` 66 | 67 | 68 | 69 | **随机访问迭代器类型处理 random_access_iterator_tag** 70 | 71 | ```c++ 72 | template 73 | inline OutputIterator 74 | __copy(RandomAccessIterator first, RandomAccessIterator last, OutputIterator result, random_access_iterator_tag) 75 | { 76 | return __copy_d(first, last, result, distance_type(first)); 77 | } 78 | 79 | template 80 | inline OutputIterator 81 | __copy_d(RandomAccessIterator first, RandomAccessIterator last, 82 | OutputIterator result, Distance*) 83 | { 84 | // 通过迭代器之间的元素个数将一个元素一个元素的复制 85 | for (Distance n = last - first; n > 0; --n, ++result, ++first) 86 | *result = *first; 87 | return result; 88 | } 89 | ``` 90 | 91 | `random_access_iterator_tag`与`input_iterator_tag`不同就在于前者不使用迭代器遍历, 后者使用的是迭代器访问. 使用迭代器的效率要低一点, 毕竟可能是RB-tree的迭代器, 链表的迭代器之类的. 92 | 93 | 94 | 95 | **全特化处理** 针对指针, `const`类型又做了特化处理 96 | 97 | ```c++ 98 | template 99 | struct __copy_dispatch 100 | { 101 | T* operator()(const T* first, const T* last, T* result) { 102 | typedef typename __type_traits::has_trivial_assignment_operator t; 103 | return __copy_t(first, last, result, t()); 104 | } 105 | }; 106 | 107 | template 108 | struct __copy_dispatch 109 | { 110 | T* operator()(T* first, T* last, T* result) { 111 | typedef typename __type_traits::has_trivial_assignment_operator t; 112 | return __copy_t(first, last, result, t()); 113 | } 114 | }; 115 | ``` 116 | 117 | 118 | 119 | 优化处理 120 | 121 | ```c++ 122 | template 123 | inline T* __copy_t(const T* first, const T* last, T* result, __false_type) { 124 | return __copy_d(first, last, result, (ptrdiff_t*) 0); 125 | } 126 | ``` 127 | 128 | 优化处理 129 | 130 | ```c++ 131 | template 132 | inline T* __copy_t(const T* first, const T* last, T* result, __true_type) { 133 | memmove(result, first, sizeof(T) * (last - first)); 134 | return result + (last - first); 135 | } 136 | ``` 137 | 138 | 139 | 140 | **针对pair类型** 141 | 142 | ```c++ 143 | template 144 | pair __copy_n(InputIterator first, Size count, 145 | OutputIterator result, 146 | input_iterator_tag) { 147 | for ( ; count > 0; --count, ++first, ++result) 148 | *result = *first; 149 | return pair(first, result); 150 | } 151 | 152 | template 153 | inline pair 154 | __copy_n(RandomAccessIterator first, Size count, 155 | OutputIterator result, 156 | random_access_iterator_tag) { 157 | RandomAccessIterator last = first + count; 158 | return pair(last, 159 | copy(first, last, result)); 160 | } 161 | ``` 162 | 163 | 164 | 165 | ### 总结 166 | 167 | 本节分析了`copy`对优化的处理, 针对不同的类型, 不同迭代器选择最优的处理函数, 提高程序的效率, STL的强大到处可以体现. `__copy_backward`我没有做分析, 他同`copy`类似, 只是反向进行复制. 168 | 169 | -------------------------------------------------------------------------------- /4 内存池.md: -------------------------------------------------------------------------------- 1 | # 内存池 2 | 3 | #### 前言 4 | 5 | 上一节只分析了第二级配置器是由多个链表来存放相同内存大小, 当没有空间的时候就向内存池索取就行了, 却没有具体分析内存池是怎么保存空间的, 是不是内存池真的有用不完的内存, 本节我们就具体来分析一下 6 | 7 | 8 | 9 | #### 内存池 10 | 11 | 12 | 13 | **static data template的初始化** 14 | 15 | ```c++ 16 | template 17 | char *__default_alloc_template::start_free = 0; // 内存池的首地址 18 | template 19 | char *__default_alloc_template::end_free = 0; // 内存池的结束地址 20 | template 21 | size_t __default_alloc_template::heap_size = 0; // 多次调用内存池, 就会更多的是给链表分配内存, 这就是一个增量. 22 | ``` 23 | 24 | 25 | 26 | 这里代码注释写的很清楚了, 我就提取出来分析一下吧 27 | 28 | > 1. 内存池的大小大于需要的空间, 直接返回起始地址(nobjs默认设置为20, 所以每次调用都会给链表额外的19个内存块) 29 | > 2. 内存池的内存不足以马上分配那么多内存, 但是还能满足分配一个即以上的大小, 那就全部分配出去 30 | > 3. 如果一个对象的大小都已经提供不了了, 先将零碎的内存块给一个小内存的链表来保存, 然后就准备调用malloc申请40块+额外大小的内存块(额外内存块就由heap_size决定), 如果申请失败跳转到步骤4, 成功跳转到步骤6 31 | > 4. 充分利用更大内存的链表, 通过递归来调用他们的内存块 32 | > 5. 如果还是没有内存块, 直接调用一级配置器来申请内存, 还是失败就抛出异常, 成功申请就继续执行 33 | > 6. 重新修改内存起始地址和结束地址为当前申请的地址块, 重新调用chunk_alloc分配内存 34 | 35 | ```c++ 36 | // 内存池 37 | template 38 | char* __default_alloc_template::chunk_alloc(size_t size, int& nobjs) 39 | { 40 | char * result; 41 | size_t total_bytes = size * nobjs; // 链表需要申请的内存大小 42 | size_t bytes_left = end_free - start_free; // 内存池里面总共还有多少内存空间 43 | 44 | // 内存池的大小大于需要的空间, 直接返回起始地址 45 | if (bytes_left >= total_bytes) 46 | { 47 | result = start_free; 48 | start_free += total_bytes; // 内存池的首地址往后移 49 | return(result); 50 | } 51 | // 内存池的内存不足以马上分配那么多内存, 但是还能满足分配一个即以上的大小, 那就按对齐方式全部分配出去 52 | else if (bytes_left >= size) 53 | { 54 | nobjs = bytes_left/size; 55 | total_bytes = size * nobjs; 56 | result = start_free; 57 | start_free += total_bytes; // 内存池的首地址往后移 58 | return(result); 59 | } 60 | else 61 | { 62 | // 如果一个对象的大小都已经提供不了了, 那就准备调用malloc申请两倍+额外大小的内存 63 | size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4); 64 | // Try to make use of the left-over piece. 65 | // 内存池还剩下的零头内存分给给其他能利用的链表, 也就是绝不浪费一点. 66 | if (bytes_left > 0) 67 | { 68 | // 链表指向申请内存的地址 69 | obj * __VOLATILE * my_free_list = free_list + FREELIST_INDEX(bytes_left); 70 | ((obj *)start_free) -> free_list_link = *my_free_list; 71 | *my_free_list = (obj *)start_free; 72 | } 73 | start_free = (char *)malloc(bytes_to_get); 74 | // 内存不足了 75 | if (0 == start_free) 76 | { 77 | int i; 78 | obj * __VOLATILE * my_free_list, *p; 79 | // 充分利用剩余链表的内存, 通过递归来申请 80 | for (i = size; i <= __MAX_BYTES; i += __ALIGN) 81 | { 82 | my_free_list = free_list + FREELIST_INDEX(i); 83 | p = *my_free_list; 84 | if (0 != p) 85 | { 86 | *my_free_list = p -> free_list_link; 87 | start_free = (char *)p; 88 | end_free = start_free + i; 89 | return(chunk_alloc(size, nobjs)); 90 | } 91 | } 92 | // 如果一点内存都没有了的话, 就只有调用一级配置器来申请内存了, 并且用户没有设置处理例程就抛出异常 93 | end_free = 0; // In case of exception. 94 | start_free = (char *)malloc_alloc::allocate(bytes_to_get); 95 | } 96 | // 申请内存成功后重新修改内存起始地址和结束地址, 重新调用chunk_alloc分配内存 97 | heap_size += bytes_to_get; 98 | end_free = start_free + bytes_to_get; 99 | return(chunk_alloc(size, nobjs)); 100 | } 101 | } 102 | ``` 103 | 104 | --- 105 | 106 | #### 总结 107 | 108 | 内存池的存在就是为了能快速的提供我们做需要的内存并且保存多余的空间, 让STL分配空间不再每次都进行malloc和free的操作, 效率又很有保障. 有时用户申请的块更小, 我们也能充分的利用起来. 唯一可能不足的是我们每次只申请`char`个大小, 但是内存池获得的确是8字节的大小. -------------------------------------------------------------------------------- /40 算法-rotate.md: -------------------------------------------------------------------------------- 1 | # rotate 2 | 3 | ### 前言 4 | 5 | `stl_algo.h`文件中有很多的算法实现, 在这里STL分析中挑选几个函数进行分析, 其他的算法大家有兴趣可以自己看看, 本节分析`stl_algo.h`文件中的`rotate`算法. 该算法实现的功能是将[first, middle),[middle, last)两段区间的元素进行交换. 6 | 7 | 8 | 9 | ### rotate分析 10 | 11 | ```c++ 12 | template 13 | inline void rotate(ForwardIterator first, ForwardIterator middle, ForwardIterator last) 14 | { 15 | if (first == middle || middle == last) return; 16 | // 三个迭代器 : first, middle, last 17 | __rotate(first, middle, last, distance_type(first), iterator_category(first)); 18 | } 19 | ``` 20 | 21 | 22 | 23 | **forward_iterator_tag** 版本 24 | 25 | 需要注意 : 26 | 27 | 1. first是前段部分的指针 28 | 2. i 表示的是后段部分的指针 29 | 30 | ```c++ 31 | template 32 | void __rotate(ForwardIterator first, ForwardIterator middle, 33 | ForwardIterator last, Distance*, forward_iterator_tag) 34 | { 35 | // 这是一个死循环, 直到满足必要条件才会退出 36 | for (ForwardIterator i = middle; ;) { 37 | // 从middle部分开始, 将两段的元素依次交换, 直到有一段被交换完才进行修改 38 | iter_swap(first, i); 39 | ++first; 40 | ++i; 41 | // 前段部分先交换完 42 | if (first == middle) { 43 | // 直到所有数据都交换完才退出 44 | if (i == last) return; 45 | // 重新修改middle的值, 让前段从之前的first = old_middle继续开始交换 46 | middle = i; 47 | } 48 | // 后段部分先交换完 49 | else if (i == last) 50 | // 重新修改i的值, 让后段从middle继续开始交换 51 | i = middle; 52 | } 53 | } 54 | ``` 55 | 56 | 整个交换的时间复杂度为O(n), 没有空间上的开销. 57 | 58 | 59 | 60 | **bidirectional_iterator_tag** 版本 61 | 62 | ```c++ 63 | template 64 | void __rotate(BidirectionalIterator first, BidirectionalIterator middle, 65 | BidirectionalIterator last, Distance*, 66 | bidirectional_iterator_tag) { 67 | reverse(first, middle); 68 | reverse(middle, last); 69 | reverse(first, last); 70 | } 71 | ``` 72 | 73 | 74 | 75 | **random_access_iterator_tag** 版本 76 | 77 | 采用的是最大公因子减少时间复杂度 78 | 79 | ```c++ 80 | template 81 | void __rotate(RandomAccessIterator first, RandomAccessIterator middle, 82 | RandomAccessIterator last, Distance*, 83 | random_access_iterator_tag) { 84 | Distance n = __gcd(last - first, middle - first); 85 | while (n--) 86 | __rotate_cycle(first, last, first + n, middle - first, value_type(first)); 87 | } 88 | template 89 | void __rotate_cycle(RandomAccessIterator first, RandomAccessIterator last, 90 | RandomAccessIterator initial, Distance shift, T*) { 91 | T value = *initial; 92 | RandomAccessIterator ptr1 = initial; 93 | RandomAccessIterator ptr2 = ptr1 + shift; 94 | while (ptr2 != initial) { 95 | *ptr1 = *ptr2; 96 | ptr1 = ptr2; 97 | if (last - ptr2 > shift) 98 | ptr2 += shift; 99 | else 100 | ptr2 = first + (shift - (last - ptr2)); 101 | } 102 | *ptr1 = value; 103 | } 104 | ``` 105 | 106 | ```c++ 107 | // 求最大公因子 108 | template 109 | EuclideanRingElement __gcd(EuclideanRingElement m, EuclideanRingElement n) 110 | { 111 | while (n != 0) { 112 | EuclideanRingElement t = m % n; 113 | m = n; 114 | n = t; 115 | } 116 | return m; 117 | } 118 | ``` 119 | 120 | 121 | 122 | ### 总结 123 | 124 | 本节分析了`rotate`实现两段元素进行交换, 整体实现并不难, 但是需要考虑不同的迭代器类型选择最优的函数实现, 最大的提高效率问题. 下一节我们将继续分析`stl_algo.h`中的其他基本的算法. -------------------------------------------------------------------------------- /41 算法--数值算法.md: -------------------------------------------------------------------------------- 1 | # 数值算法 2 | 3 | ### 前言 4 | 5 | 本节所分析的算法是在`stl_numeric.h`中, 本节以源码 + 例子的形式一起分析这些实现的操作. 6 | 7 | 8 | 9 | ### 数值算法分析 10 | 11 | 12 | 13 | #### accumulate 14 | 15 | 要求传入两个`InputIterator`类型的迭代器和一个初始化值, 第二个版本还支持在传入一个仿函数或函数. 16 | 17 | ```c++ 18 | // 版本一 19 | template 20 | T accumulate(InputIterator first, InputIterator last, T init) { 21 | for ( ; first != last; ++first) 22 | init = init + *first; 23 | return init; 24 | } 25 | // 版本二 : 接受函数或仿函数 26 | template 27 | T accumulate(InputIterator first, InputIterator last, T init, 28 | BinaryOperation binary_op) { 29 | for ( ; first != last; ++first) 30 | init = binary_op(init, *first); 31 | return init; 32 | } 33 | ``` 34 | 35 | 实例: 36 | 37 | ```c++ 38 | // 函数 39 | inline int binary(int a1, int a2) 40 | { 41 | return a1 - a2; 42 | } 43 | // 仿函数 44 | struct mybinary 45 | { 46 | int operator() (const int a1, const int a2) 47 | { 48 | return a1 + a2; 49 | } 50 | }mybinary; 51 | 52 | int main() 53 | { 54 | int a[4]= {1, 2, 3, 4}; 55 | int sum = 0; 56 | sum = accumulate(a, a+4, sum); // 版本一 57 | for(const auto &i : a) 58 | cout << i << " "; // 1 2 3 4 59 | cout << sum; // 10 60 | 61 | sum = accumulate(a, a+4, sum, binary); // 版本二, 函数 62 | cout << sum; // 0 63 | 64 | sum = accumulate(a, a+4, sum, mybinary); // 版本二, 仿函数 65 | cout << sum; // 10 66 | 67 | exit(0); 68 | } 69 | ``` 70 | 71 | 72 | 73 | #### inner_product 74 | 75 | 两个版本. 76 | 77 | 版本一 : 两个`InputIterator`类型的迭代器, 一个迭代器(所有的数据 * first2), 一个初始化值. 78 | 79 | ```c++ 80 | template 81 | T inner_product(InputIterator1 first1, InputIterator1 last1, 82 | InputIterator2 first2, T init) { 83 | for ( ; first1 != last1; ++first1, ++first2) 84 | init = init + (*first1 * *first2); 85 | return init; 86 | } 87 | ``` 88 | 89 | 版本二 : 比版本一多接受两个函数或仿函数, 先执行`binary_op2`操作再执行`binary_op1`. 与上面的accumulate一样. 90 | 91 | ```c++ 92 | template 94 | T inner_product(InputIterator1 first1, InputIterator1 last1, 95 | InputIterator2 first2, T init, BinaryOperation1 binary_op1, 96 | BinaryOperation2 binary_op2) { 97 | for ( ; first1 != last1; ++first1, ++first2) 98 | init = binary_op1(init, binary_op2(*first1, *first2)); 99 | return init; 100 | } 101 | ``` 102 | 103 | 104 | 105 | #### partial_sum 106 | 107 | 两个版本. 功能 : 将传入的`InputIterator`两个迭代器范围内的值进行局部求和. 即 y0 = x0; y1 = x0 + x1; ... 108 | 109 | 版本一 : 默认操作 110 | 111 | ```c++ 112 | template 113 | OutputIterator __partial_sum(InputIterator first, InputIterator last, 114 | OutputIterator result, T*) { 115 | T value = *first; 116 | // 跳过x0, 从x1开始局部求和 117 | while (++first != last) { 118 | value = value + *first; 119 | *++result = value; 120 | } 121 | return ++result; 122 | } 123 | 124 | template 125 | OutputIterator partial_sum(InputIterator first, InputIterator last, 126 | OutputIterator result) { 127 | if (first == last) return result; 128 | *result = *first; 129 | return __partial_sum(first, last, result, value_type(first)); 130 | } 131 | ``` 132 | 133 | 版本二 : 比版本一多了一个二元操作. 134 | 135 | ```c++ 136 | template 138 | OutputIterator __partial_sum(InputIterator first, InputIterator last, 139 | OutputIterator result, T*, 140 | BinaryOperation binary_op) { 141 | T value = *first; 142 | // 跳过x0, 从x1开始局部求和 143 | while (++first != last) { 144 | value = binary_op(value, *first); 145 | *++result = value; 146 | } 147 | return ++result; 148 | } 149 | 150 | template 151 | OutputIterator partial_sum(InputIterator first, InputIterator last, 152 | OutputIterator result, BinaryOperation binary_op) { 153 | if (first == last) return result; 154 | *result = *first; 155 | return __partial_sum(first, last, result, value_type(first), binary_op); 156 | } 157 | ``` 158 | 159 | 160 | 161 | 实例: 162 | 163 | ```c++ 164 | inline int partial_inc(int a1, int a2) 165 | { 166 | return a2+2; 167 | } 168 | 169 | int main() 170 | { 171 | int a[4] = {1, 2, 3, 4 }; 172 | int a2[4]; 173 | 174 | partial_sum(a, a+sizeof(a)/sizeof(int), a2); // 版本一 175 | for(const auto &i : a) 176 | cout << i << " "; // 1 2 3 4 177 | 178 | for(const auto &i : a2) 179 | cout << i << " "; // 1 3 6 10 180 | 181 | partial_sum(a, a+sizeof(a)/sizeof(int), a2, partial_inc); // 版本二 182 | for(const auto &i : a2) 183 | cout << i << " "; // 1 4 5 6 184 | 185 | exit(0); 186 | } 187 | ``` 188 | 189 | 190 | 191 | #### adjacent_difference 192 | 193 | 两个版本. 功能 : 将传入的`InputIterator`两个迭代器范围内的值进行局部相减. 即 y0 = x0; y1 = x0 - x1 ... 194 | 195 | 版本一 196 | 197 | ```c++ 198 | template 199 | OutputIterator __adjacent_difference(InputIterator first, InputIterator last, 200 | OutputIterator result, T*) { 201 | T value = *first; 202 | while (++first != last) { 203 | T tmp = *first; 204 | *++result = tmp - value; // 进行相减 205 | value = tmp; 206 | } 207 | return ++result; 208 | } 209 | 210 | template 211 | OutputIterator adjacent_difference(InputIterator first, InputIterator last, 212 | OutputIterator result) { 213 | if (first == last) return result; 214 | *result = *first; 215 | return __adjacent_difference(first, last, result, value_type(first)); 216 | } 217 | ``` 218 | 219 | 220 | 221 | 版本二 : 指定二元操作 222 | 223 | ```c++ 224 | template 225 | OutputIterator __adjacent_difference(InputIterator first, InputIterator last, 226 | OutputIterator result, T*, 227 | BinaryOperation binary_op) { 228 | T value = *first; 229 | while (++first != last) { 230 | T tmp = *first; 231 | *++result = binary_op(tmp, value); 232 | value = tmp; 233 | } 234 | return ++result; 235 | } 236 | 237 | template 238 | OutputIterator adjacent_difference(InputIterator first, InputIterator last, 239 | OutputIterator result, 240 | BinaryOperation binary_op) { 241 | if (first == last) return result; 242 | *result = *first; 243 | return __adjacent_difference(first, last, result, value_type(first), 244 | binary_op); 245 | } 246 | ``` 247 | 248 | 249 | 250 | 实例: 251 | 252 | ```c++ 253 | struct adjacent{ 254 | int operator() (const int tmp, const int a) { return tmp - a - a; } 255 | }adjacent; 256 | 257 | int main() 258 | { 259 | int a[4] = {1, 2, 3, 4}; 260 | int a2[4]; 261 | 262 | for(const auto &i : a) 263 | cout << i << " "; // 1 2 3 4 264 | 265 | adjacent_difference(a, a+4, a2); 266 | for(const auto &i : a2) 267 | cout << i << " "; // 1 1 1 1 268 | 269 | partial_sum(a2, a2+4, a2); 270 | for(const auto &i : a2) 271 | cout << i << " "; // 1 2 3 4 272 | 273 | adjacent_difference(a, a+4, a2, adjacent); 274 | for(const auto &i : a2) 275 | cout << i << " "; // 1 0 -1 -2 276 | 277 | exit(0); 278 | } 279 | ``` 280 | 281 | 从实例可以看出来`adjacent_difference`与`partial_sum`相结合可以恢复. 282 | 283 | 284 | 285 | #### power 286 | 287 | 一个版本. 幂次方。如果指定为乘法运算,则当n >= 0 时传回 x^n 288 | 289 | ```c++ 290 | // Returns x ** n, where n >= 0. Note that "multiplication" 291 | // is required to be associative, but not necessarily commutative. 292 | template 293 | T power(T x, Integer n, MonoidOperation op) { 294 | if (n == 0) 295 | return identity_element(op); 296 | else { 297 | while ((n & 1) == 0) { 298 | n >>= 1; 299 | x = op(x, x); 300 | } 301 | 302 | T result = x; 303 | n >>= 1; 304 | while (n != 0) { 305 | x = op(x, x); 306 | if ((n & 1) != 0) 307 | result = op(result, x); 308 | n >>= 1; 309 | } 310 | return result; 311 | } 312 | } 313 | 314 | template 315 | inline T power(T x, Integer n) { 316 | return power(x, n, multiplies()); 317 | } 318 | ``` 319 | 320 | 321 | 322 | #### iota 323 | 324 | 设定某个区间的内容, 使其每个元素从指定值value开始, 呈现递增 325 | 326 | ```c++ 327 | template 328 | void iota(ForwardIterator first, ForwardIterator last, T value) { 329 | while (first != last) *first++ = value++; 330 | } 331 | ``` 332 | 333 | 实例: 334 | 335 | ```c++ 336 | int main() 337 | { 338 | int a[4] = {1, 2, 3, 4}; 339 | iota(a+1, a+4, 10); 340 | for(const auto &i : a) 341 | cout << i << ' '; // 1 10 11 12 342 | 343 | return 0; 344 | } 345 | ``` 346 | 347 | 348 | 349 | ### 总结 350 | 351 | 本节对`numeric.h`中的算法进行了分析, 这些都是很简单的函数实现, 但是大都提供了两个版本, 可接受二元操作的函数, 这由用户定义, 也就提高了STL的灵活性. -------------------------------------------------------------------------------- /42 算法-stl_algo.h基本算法.md: -------------------------------------------------------------------------------- 1 | # stl_algo.h中的基本算法 2 | 3 | ### 前言 4 | 5 | 上一节分析了`stl_algo.h`中的rotate函数实现, 本节我们继续分析该文件的中的其他基本算法, 这些算法功能实现看似很简单, 但是这些算法都能进行衍生, 用户自定义, 简单化了我们的部分编程, 直接使用无需再定义. 6 | 7 | 8 | 9 | ### 基本算法 10 | 11 | 12 | 13 | #### for_each 14 | 15 | 将[first, last) 范围的元素由传入仿函数(函数)进行处理, 最后返回仿函数(函数). 16 | 17 | ```c++ 18 | template 19 | Function for_each(InputIterator first, InputIterator last, Function f) { 20 | for ( ; first != last; ++first) 21 | f(*first); // 传入的是右值 22 | return f; 23 | } 24 | ``` 25 | 26 | 实例: 27 | 28 | ```c++ 29 | void For_each(int i) { i = 1; } 30 | 31 | void For_func(int i) { cout << i << " "; } 32 | 33 | int main() 34 | { 35 | vector a(10); 36 | for_each(a.begin(), a.end(), For_each); // 尝试初始化值 37 | for(const auto &i : a) 38 | cout << i << " "; // 0 0 0 0 0 0 0 0 0 0 39 | for_each(a.begin(), a.end(), For_func); // 0 0 0 0 0 0 0 0 0 0 40 | 41 | return 0; 42 | } 43 | ``` 44 | 45 | 很明显尝试初始化值是有问题的, 因为`for_each`不会修改传入的值, 传入的是右值, 所以一般使用`for_each`都是实现迭代器的输出, 这样就不用写for循环再来输出了. 46 | 47 | 48 | 49 | #### find 50 | 51 | 两个版本. 52 | 53 | ```c++ 54 | // 版本一 55 | template 56 | InputIterator find(InputIterator first, InputIterator last, const T& value) { 57 | while (first != last && *first != value) ++first; 58 | return first; 59 | } 60 | // 版本二 61 | template 62 | InputIterator find_if(InputIterator first, InputIterator last, 63 | Predicate pred) { 64 | while (first != last && !pred(*first)) ++first; // 一元操作, 自定义判断条件 65 | return first; 66 | } 67 | ``` 68 | 69 | 70 | 71 | #### adjacent_find 72 | 73 | 两个版本. 74 | 75 | 第一个版本找出相邻元素间第一个出现元素相同的情况, 返回第一次出现的迭代器 76 | 77 | 第二个版本找出相邻元素间第一个满足仿函数条件的情况, 返回第一次满足情况的迭代器 78 | 79 | ```c++ 80 | // 第一个版本找出相邻元素间第一个出现元素相同的情况, 返回第一次出现的迭代器 81 | template 82 | ForwardIterator adjacent_find(ForwardIterator first, ForwardIterator last) { 83 | if (first == last) return last; 84 | ForwardIterator next = first; 85 | while(++next != last) { 86 | if (*first == *next) return first; 87 | first = next; 88 | } 89 | return last; 90 | } 91 | // 第二个版本找出相邻元素间第一个满足仿函数条件的情况, 返回第一次满足情况的迭代器 92 | template 93 | ForwardIterator adjacent_find(ForwardIterator first, ForwardIterator last, 94 | BinaryPredicate binary_pred) { 95 | if (first == last) return last; 96 | ForwardIterator next = first; 97 | while(++next != last) { 98 | if (binary_pred(*first, *next)) return first; 99 | first = next; 100 | } 101 | return last; 102 | } 103 | ``` 104 | 105 | 106 | 107 | #### count 108 | 109 | 两个版本 110 | 111 | 版本一 : 统计[first, last)范围出现的指定元素的次数 112 | 113 | 版本二 : 统计[first, last)范围满足条件的元素的次数 114 | 115 | ```c++ 116 | // 版本一 : 统计[first, last)范围出现的指定元素的次数 117 | template 118 | void count(InputIterator first, InputIterator last, const T& value, Size& n) { 119 | for ( ; first != last; ++first) 120 | if (*first == value) 121 | ++n; 122 | } 123 | // 版本二 : 统计[first, last)范围满足条件的元素的次数 124 | template 125 | void count_if(InputIterator first, InputIterator last, Predicate pred, Size& n) { 126 | for ( ; first != last; ++first) 127 | if (pred(*first)) 128 | ++n; 129 | } 130 | 131 | #ifdef __STL_CLASS_PARTIAL_SPECIALIZATION 132 | // 版本一 : 统计[first, last)范围出现的指定元素的次数 133 | template 134 | typename iterator_traits::difference_type 135 | count(InputIterator first, InputIterator last, const T& value) { 136 | typename iterator_traits::difference_type n = 0; 137 | for ( ; first != last; ++first) 138 | if (*first == value) 139 | ++n; 140 | return n; 141 | } 142 | // 版本二 : 统计[first, last)范围满足条件的元素的次数 143 | template 144 | typename iterator_traits::difference_type 145 | count_if(InputIterator first, InputIterator last, Predicate pred) { 146 | typename iterator_traits::difference_type n = 0; 147 | for ( ; first != last; ++first) 148 | if (pred(*first)) 149 | ++n; 150 | return n; 151 | } 152 | ``` 153 | 154 | 155 | 156 | #### replace 157 | 158 | 两个版本. 159 | 160 | 版本一 : 将所有是old_value的数据换成new_value 161 | 162 | 版本二 : 将所有是满足仿函数(函数)的数据换成new_value 163 | 164 | ```c++ 165 | // 版本一 : 将所有是old_value的数据换成new_value 166 | template 167 | void replace(ForwardIterator first, ForwardIterator last, const T& old_value, 168 | const T& new_value) { 169 | for ( ; first != last; ++first) 170 | if (*first == old_value) *first = new_value; 171 | } 172 | // 版本二 : 将所有是满足仿函数的数据换成new_value 173 | template 174 | void replace_if(ForwardIterator first, ForwardIterator last, Predicate pred, 175 | const T& new_value) { 176 | for ( ; first != last; ++first) 177 | if (pred(*first)) *first = new_value; 178 | } 179 | ``` 180 | 181 | 182 | 183 | #### replace_copy 184 | 185 | 两个版本. 两个版本都不会修改原容器的数据 186 | 187 | 版本一 : 将所有元素保存在另个容器中, 并将所有是old_value的数据换成new_value 188 | 189 | 版本二 : 将所有元素保存在另个容器中, 并将所有满足仿函数的数据换成new_value 190 | 191 | ```c++ 192 | // 将所有元素保存在另个容器中, 并将所有是old_value的数据换成new_value 193 | template 194 | OutputIterator replace_copy(InputIterator first, InputIterator last, 195 | OutputIterator result, const T& old_value, 196 | const T& new_value) { 197 | for ( ; first != last; ++first, ++result) 198 | *result = *first == old_value ? new_value : *first; 199 | return result; 200 | } 201 | // 将所有元素保存在另个容器中, 并将所有满足仿函数的数据换成new_value 202 | template 203 | OutputIterator replace_copy_if(Iterator first, Iterator last, 204 | OutputIterator result, Predicate pred, 205 | const T& new_value) { 206 | for ( ; first != last; ++first, ++result) 207 | *result = pred(*first) ? new_value : *first; 208 | return result; 209 | } 210 | ``` 211 | 212 | 213 | 214 | ### 总结 215 | 216 | 本节分析了`stl_algo.h`的基本算法的实现, 可以简化平时程序的代码量. 217 | 218 | -------------------------------------------------------------------------------- /43 算法-二分查找.md: -------------------------------------------------------------------------------- 1 | # 二分查找 2 | 3 | ### 前言 4 | 5 | 前面我们分析了关于`stl_algo.h`中的基本算法, 本节也将继续分析该文件中的算法实现, 即二分查找. 6 | 7 | `stl_algo.h`提供了`lower_bound`和`upper_bound`两种查找, 前者是找出第一个满足条件的迭代器, 后者则是找出最后一个满足条件的迭代器, 两者结合就能知道该容器中重复的个数. 8 | 9 | 10 | 11 | ### 二分查找分析 12 | 13 | #### lower_bound 14 | 15 | **ForwardIterator**版本 16 | 17 | ```c++ 18 | // 版本一 19 | template 20 | inline ForwardIterator lower_bound(ForwardIterator first, ForwardIterator last, const T& value) 21 | { 22 | return __lower_bound(first, last, value, distance_type(first), iterator_category(first)); 23 | } 24 | 25 | template 26 | ForwardIterator __lower_bound(ForwardIterator first, ForwardIterator last, 27 | const T& value, Distance*, 28 | forward_iterator_tag) { 29 | Distance len = 0; 30 | distance(first, last, len); 31 | Distance half; 32 | ForwardIterator middle; 33 | 34 | while (len > 0) { 35 | half = len >> 1; // 相当于除2 36 | // middle为区间的起始位置 37 | middle = first; 38 | // 设置middle为区间的中间值 39 | advance(middle, half); 40 | // 将value值与中间值比较, 即是二分查找, 若中间值小于value, 则继续查找右半部分 41 | if (*middle < value) { 42 | first = middle; 43 | ++first; 44 | len = len - half - 1; 45 | } 46 | else 47 | len = half; 48 | } 49 | return first; 50 | } 51 | 52 | // 版本二 53 | template 54 | inline ForwardIterator lower_bound(ForwardIterator first, ForwardIterator last, 55 | const T& value, Compare comp) { 56 | return __lower_bound(first, last, value, comp, distance_type(first), iterator_category(first)); 57 | } 58 | 59 | template 60 | ForwardIterator __lower_bound(ForwardIterator first, ForwardIterator last, 61 | const T& value, Compare comp, Distance*, 62 | forward_iterator_tag) { 63 | Distance len = 0; 64 | distance(first, last, len); 65 | Distance half; 66 | ForwardIterator middle; 67 | 68 | while (len > 0) { 69 | half = len >> 1; 70 | middle = first; 71 | // 设置middle为区间的中间值 72 | advance(middle, half); 73 | // 满足的条件的进行比赛 74 | if (comp(*middle, value)) { 75 | first = middle; 76 | ++first; 77 | len = len - half - 1; 78 | } 79 | else 80 | len = half; 81 | } 82 | return first; 83 | } 84 | ``` 85 | 86 | 87 | 88 | **RandomAccessIterator** 版本 89 | 90 | ```c++ 91 | // 版本一 92 | template 93 | RandomAccessIterator __lower_bound(RandomAccessIterator first, 94 | RandomAccessIterator last, const T& value, 95 | Distance*, random_access_iterator_tag) { 96 | Distance len = last - first; 97 | Distance half; 98 | RandomAccessIterator middle; 99 | // 二分法 100 | while (len > 0) { 101 | half = len >> 1; // 相当于除2 102 | // 设置middle为区间的中间值. 103 | // 这里直接求出middle的值, 更加的快速 104 | middle = first + half; 105 | if (*middle < value) { 106 | first = middle + 1; 107 | len = len - half - 1; 108 | } 109 | else 110 | len = half; 111 | } 112 | return first; 113 | } 114 | // 版本二 115 | template 116 | RandomAccessIterator __lower_bound(RandomAccessIterator first, 117 | RandomAccessIterator last, 118 | const T& value, Compare comp, Distance*, 119 | random_access_iterator_tag) { 120 | Distance len = last - first; 121 | Distance half; 122 | RandomAccessIterator middle; 123 | 124 | while (len > 0) { 125 | half = len >> 1; 126 | // 设置middle为区间的中间值. 127 | // 这里直接求出middle的值, 更加的快速 128 | middle = first + half; 129 | if (comp(*middle, value)) { 130 | first = middle + 1; 131 | len = len - half - 1; 132 | } 133 | else 134 | len = half; 135 | } 136 | return first; 137 | } 138 | ``` 139 | 140 | 141 | 142 | #### upper_bound 143 | 144 | 这里就只分析`ForwardIterator`就行了, `RandomAccessIterator`也跟上面一样的操作 145 | 146 | ```c++ 147 | // 版本一 148 | template 149 | inline ForwardIterator upper_bound(ForwardIterator first, ForwardIterator last, 150 | const T& value) { 151 | return __upper_bound(first, last, value, distance_type(first), 152 | iterator_category(first)); 153 | } 154 | 155 | template 156 | ForwardIterator __upper_bound(ForwardIterator first, ForwardIterator last, 157 | const T& value, Compare comp, Distance*, 158 | forward_iterator_tag) { 159 | Distance len = 0; 160 | distance(first, last, len); 161 | Distance half; 162 | ForwardIterator middle; 163 | 164 | while (len > 0) { 165 | half = len >> 1; // 相当于除2 166 | // middle为区间的起始位置 167 | middle = first; 168 | // 设置middle为区间的中间值 169 | advance(middle, half); 170 | // 将value值与中间值比较, 即是二分查找, 若中间值小于value, 则继续查找右半部分 171 | if (comp(value, *middle)) 172 | len = half; 173 | else { 174 | first = middle; 175 | ++first; 176 | len = len - half - 1; 177 | } 178 | } 179 | return first; 180 | } 181 | // 版本二 182 | template 183 | inline ForwardIterator upper_bound(ForwardIterator first, ForwardIterator last, 184 | const T& value, Compare comp) { 185 | return __upper_bound(first, last, value, comp, distance_type(first), 186 | iterator_category(first)); 187 | } 188 | ``` 189 | 190 | 191 | 192 | ### 总结 193 | 194 | 关于STL的二分查找让我学习到以前自己实现的有很多的问题, 没有这样的区分重复的数据, 而是直接找到就返回, 而STL对其有两种不同的选择, 可以找到第一出现, 也可以找到最后一次出现, 学习STL总能让人不禁易认识很多东西, 学到很多细节的处理与完善. 关于`stl_algo.h`中的排序我暂时不再继续分析了, 等之后有时间再来重新总结. 195 | 196 | 知乎有对STL泛型算法中`sort`的效率的讨论[stl的sort和手写快排的运行效率哪个比较高](https://www.zhihu.com/question/24189847/answer/130319417) -------------------------------------------------------------------------------- /44 仿函数.md: -------------------------------------------------------------------------------- 1 | # 仿函数(函数对象) 2 | 3 | ## 前言 4 | 5 | 本节将分析STL六部分之一的仿函数, 在前面分析容器以及算法的时候都有使用过仿函数, 在算法中我们还自定义过仿函数(函数对象), 就是使用`struct`或者`class`重载运算符, 就是**行为类似于函数的对象.** 6 | 7 | - 根据仿函数的操作数可分为 : 一元和二元仿函数. 8 | 9 | - 根据功能可分为 : 算术运算, 关系运算, 逻辑运算. 10 | 11 | 12 | 13 | ### 仿函数与函数指针 14 | 15 | STL采用对象重载操作实现函数行为并没有直接定义函数指针来代替仿函数, 主要有几点原因让STL选择仿函数 16 | 17 | 1. 函数指针不能满足STL的抽象性 18 | 2. 函数指针不能与STL的其他组件搭配, 不够灵活 19 | 3. 仿函数具有可配接性, 可以满足`traits`编程, 也是能在编译期间就能完成, 不会有运行时的开销 20 | 21 | 22 | 23 | ### 仿函数分析 24 | 25 | 26 | 27 | #### 一元仿函数 28 | 29 | **一元仿函数基类(unary_function)** 30 | 31 | ```c++ 32 | template 33 | struct unary_function { 34 | typedef Arg argument_type; // 参数类型别名 35 | typedef Result result_type; // 返回值类型别名 36 | }; 37 | ``` 38 | 39 | #### 二元仿函数 40 | 41 | **二元仿函数基类(binary_function)** 42 | 43 | ```c++ 44 | template 45 | struct binary_function { 46 | typedef Arg1 first_argument_type; // 参数类型别名 47 | typedef Arg2 second_argument_type; // 参数类型别名 48 | typedef Result result_type; // 返回值类型别名 49 | }; 50 | ``` 51 | 52 | 53 | 54 | #### 算术运算 55 | 56 | 每个仿函数都继承了二元仿函数, 仿函数可以通过继承使STL更灵活, 函数指针无法这样灵活的操作 57 | 58 | - **加法** 59 | 60 | ```c++ 61 | template 62 | struct plus : public binary_function { 63 | T operator()(const T& x, const T& y) const { return x + y; } 64 | }; 65 | ``` 66 | 67 | - **乘法** 68 | 69 | ```c++ 70 | template 71 | struct multiplies : public binary_function { 72 | T operator()(const T& x, const T& y) const { return x * y; } 73 | }; 74 | ``` 75 | 76 | - **除法** 77 | 78 | ```c++ 79 | template 80 | struct divides : public binary_function { 81 | T operator()(const T& x, const T& y) const { return x / y; } 82 | }; 83 | ``` 84 | 85 | - **减法** 86 | 87 | ```c++ 88 | template 89 | struct minus : public binary_function { 90 | T operator()(const T& x, const T& y) const { return x - y; } 91 | }; 92 | ``` 93 | 94 | - **取模** 95 | 96 | ```c++ 97 | template 98 | struct modulus : public binary_function { 99 | T operator()(const T& x, const T& y) const { return x % y; } 100 | }; 101 | ``` 102 | 103 | - **相反** 104 | 105 | ```c++ 106 | // 继承的是一元仿函数 107 | template 108 | struct negate : public unary_function { 109 | T operator()(const T& x) const { return -x; } 110 | }; 111 | ``` 112 | 113 | **正同元素** 114 | 115 | 正同元素 : 数值A与该元素做op得到的依然还是A. 116 | 117 | ```c++ 118 | template inline T identity_element(plus) { return T(0); } 119 | // 在stl_numeric.h的power中有使用 120 | template inline T identity_element(multiplies) { return T(1); } 121 | ``` 122 | 123 | 124 | 125 | #### 关系运算类 126 | 127 | - 等于 128 | 129 | ```c++ 130 | template 131 | struct equal_to : public binary_function { 132 | bool operator()(const T& x, const T& y) const { return x == y; } 133 | }; 134 | ``` 135 | 136 | - 不等于 137 | 138 | ```c++ 139 | template 140 | struct not_equal_to : public binary_function { 141 | bool operator()(const T& x, const T& y) const { return x != y; } 142 | }; 143 | ``` 144 | 145 | - 大于 146 | 147 | ```c++ 148 | template 149 | struct greater : public binary_function { 150 | bool operator()(const T& x, const T& y) const { return x > y; } 151 | }; 152 | ``` 153 | 154 | - 小于 155 | 156 | ```c++ 157 | template 158 | struct less : public binary_function { 159 | bool operator()(const T& x, const T& y) const { return x < y; } 160 | }; 161 | ``` 162 | 163 | ... 164 | 165 | 166 | 167 | #### 逻辑运算类 168 | 169 | - 或 170 | 171 | ```c++ 172 | template 173 | struct logical_or : public binary_function { 174 | bool operator()(const T& x, const T& y) const { return x || y; } 175 | }; 176 | ``` 177 | 178 | - 与 179 | 180 | ```c++ 181 | template 182 | struct logical_and : public binary_function { 183 | bool operator()(const T& x, const T& y) const { return x && y; } 184 | }; 185 | ``` 186 | 187 | - 非 188 | 189 | ```c++ 190 | template 191 | struct logical_not : public unary_function { 192 | bool operator()(const T& x) const { return !x; } 193 | }; 194 | ``` 195 | 196 | 197 | 198 | #### 正同,选择,投射 199 | 200 | 正同 : 任何数值调用该函数都不会有改变 201 | 202 | 选择 : 接受一个`pair`类型, 并且返回第一个元素或者第二个元素 203 | 204 | 投影 : 接受一个`pair`类型, 忽略第二个元素返回第一个元素或者忽略第一个元素返回第二个元素 205 | 206 | - 正同 207 | 208 | ```c++ 209 | template 210 | struct identity : public unary_function { 211 | const T& operator()(const T& x) const { return x; } 212 | }; 213 | ``` 214 | 215 | - 选择 216 | 217 | ```c++ 218 | // 选择第一个元素 219 | template 220 | struct select1st : public unary_function { 221 | const typename Pair::first_type& operator()(const Pair& x) const 222 | { 223 | return x.first; 224 | } 225 | }; 226 | // 选择第二个元素 227 | template 228 | struct select2nd : public unary_function { 229 | const typename Pair::second_type& operator()(const Pair& x) const 230 | { 231 | return x.second; 232 | } 233 | }; 234 | ``` 235 | 236 | - 投影 237 | 238 | ```c++ 239 | // 忽略第二个元素返回第一个元素 240 | template 241 | struct project1st : public binary_function { 242 | Arg1 operator()(const Arg1& x, const Arg2&) const { return x; } 243 | }; 244 | // 忽略第一个元素返回第二个元素 245 | template 246 | struct project2nd : public binary_function { 247 | Arg2 operator()(const Arg1&, const Arg2& y) const { return y; } 248 | }; 249 | ``` 250 | 251 | 252 | ### 总结 253 | 254 | 仿函数使STL变得非常的灵活, 用户也可以自定义, 并且提供接口关联配接器, 下一节我们就分析STL最后的组件--配接器. -------------------------------------------------------------------------------- /45 配接器.md: -------------------------------------------------------------------------------- 1 | # 配接器 2 | 3 | ### 前言 4 | 5 | 本节分析STL组件之一的配接器, 配接器就像转接口一样, 将一个对class封装变成了另外一个功能的class, 这在前面分析容器的时候遇到过, 比如: map, set等, 都是修改底层接口形成另外一个功能的class. 6 | 7 | 配接器可以应用于 : 仿函数, 迭代器, 容器. 8 | 9 | 关于配接器应用于容器前面都分析过了, 接下来就分析部分应用于仿函数和迭代器的配接器吧. 10 | 11 | 12 | 13 | ### 应用于迭代器 14 | 15 | STL实现了很多应用于迭代器上的配接器, 比如 : `reverse iterators`,`iostream iterators`等, 这里就选择前两个进行分析 16 | 17 | **reverse iterators** 18 | 19 | ```c++ 20 | template 21 | class reverse_iterator 22 | { 23 | protected: 24 | Iterator current; 25 | public: 26 | // 反向迭代器的5种相应型别和正向的正向迭代器相同 27 | typedef typename iterator_traits::iterator_category iterator_category; 28 | typedef typename iterator_traits::value_type value_type; 29 | typedef typename iterator_traits::difference_type difference_type; 30 | typedef typename iterator_traits::pointer pointer; 31 | typedef typename iterator_traits::reference reference; 32 | 33 | // 正向迭代器 34 | typedef Iterator iterator_type; 35 | // 反向迭代器 36 | typedef reverse_iterator self; 37 | 38 | public: 39 | // 构造函数 40 | reverse_iterator() {} 41 | // 将反向迭代器与某种迭代器联系起来 42 | explicit reverse_iterator(iterator_type x) : current(x) {} 43 | reverse_iterator(const self& x) : current(x.current) {} 44 | 45 | // 返回正向迭代器 46 | iterator_type base() const { return current; } 47 | // 反向迭代器取值时,先将正向迭代器后退一位,再取值 48 | reference operator*() const { 49 | Iterator tmp = current; 50 | return *--tmp; 51 | } 52 | // 反转后end变成了begin. begin变成了end 53 | // 注意一点就是end是指向最后一个元素的后一个位置 54 | self& operator++() { 55 | --current; 56 | return *this; 57 | } 58 | self operator++(int) { 59 | self tmp = *this; 60 | --current; 61 | return tmp; 62 | } 63 | self& operator--() { 64 | ++current; 65 | return *this; 66 | } 67 | self operator--(int) { 68 | self tmp = *this; 69 | ++current; 70 | return tmp; 71 | } 72 | 73 | // 前进和后退方向相反 74 | self operator+(difference_type n) const { 75 | return self(current - n); 76 | } 77 | self& operator+=(difference_type n) { 78 | current -= n; 79 | return *this; 80 | } 81 | self operator-(difference_type n) const { 82 | return self(current + n); 83 | } 84 | self& operator-=(difference_type n) { 85 | current += n; 86 | return *this; 87 | } 88 | reference operator[](difference_type n) const { return *(*this + n); } 89 | }; 90 | ``` 91 | 92 | 93 | 94 | **iostream iterators** 95 | 96 | ```c++ 97 | template 98 | class istream_iterator { 99 | // 定义友元 100 | friend bool 101 | operator== __STL_NULL_TMPL_ARGS (const istream_iterator& x, 102 | const istream_iterator& y); 103 | protected: 104 | istream* stream; // 定义istream指针 105 | T value; 106 | bool end_marker; 107 | // 执行读 108 | void read() { 109 | end_marker = (*stream) ? true : false; 110 | // 当有数据等待就进行读操作 111 | if (end_marker) *stream >> value; 112 | // 当读取到不符合型别或者eof时返回false, 不再读取 113 | end_marker = (*stream) ? true : false; 114 | } 115 | public: 116 | // 类型定义, 满足traits编程 117 | typedef input_iterator_tag iterator_category; 118 | typedef T value_type; 119 | typedef Distance difference_type; 120 | typedef const T* pointer; 121 | typedef const T& reference; 122 | 123 | istream_iterator() : stream(&cin), end_marker(false) {} // 默认传入cin指针 124 | istream_iterator(istream& s) : stream(&s) { read(); } 125 | reference operator*() const { return value; } 126 | #ifndef __SGI_STL_NO_ARROW_OPERATOR 127 | pointer operator->() const { return &(operator*()); } 128 | #endif /* __SGI_STL_NO_ARROW_OPERATOR */ 129 | // 这里迭代器++表示将要读取到下一个数据 130 | istream_iterator& operator++() { 131 | read(); 132 | return *this; 133 | } 134 | istream_iterator operator++(int) { 135 | istream_iterator tmp = *this; 136 | read(); 137 | return tmp; 138 | } 139 | }; 140 | ``` 141 | 142 | 143 | 144 | 145 | 146 | ### 应用于仿函数 147 | 148 | 要实现仿函数可配接就需要继承`unary_function`或`binary_function`. 149 | 150 | 上一节仿函数所分析的"函数"都可用于配接. 151 | 152 | ```c++ 153 | template 154 | inline binder1st bind1st(const Operation& op, const T& x) { 155 | typedef typename Operation::first_argument_type arg1_type; 156 | return binder1st(op, arg1_type(x)); 157 | } 158 | ``` 159 | 160 | 161 | 162 | ### 总结 163 | 164 | 本节对STL的配接器的部分函数进行了分析, `stl_function.h`和`stl_iterator.h`文件里面还有很多函数, 有兴趣可以好好看看, 这里也只是简单的分析. **源码面前没有秘密.** -------------------------------------------------------------------------------- /5 迭代器.md: -------------------------------------------------------------------------------- 1 | # 迭代器 2 | 3 | ### 前言 4 | 5 | 迭代器是将算法和容器两个独立的泛型进行调和的一个接口. 使我们不需要关系中间的转化是怎么样的就都能直接使用迭代器进行数据访问. 而迭代器最重要的就是对`operator *`和`operator->`进行重载, 使它表现的像一个指针. 6 | 7 | ### 类型 8 | 9 | 迭代器根据移动特性和实施操作被分为5类 10 | 11 | > 1. input iterator(输入迭代器) : 迭代器所指的内容不能被修改, **只读且只能执行一次读操作.** 12 | > 2. output iterator(输出迭代器) : 只写并且一次只能执行一次写操作. 13 | > 3. forward iterator(正向迭代器) : 支持读写操作且支持多次读写操作. 14 | > 4. bidirectional iterator(双向迭代器) : 支持双向的移动且支持多次读写操作. 15 | > 5. random access iterator(随即访问迭代器) : 支持双向移动且支持多次读写操作. p+n, p-n等. 16 | > 17 | > 1~4类迭代器执行操作的就如 : p++, ++p, p->而不是5类的p+n操作. 不明白的我们下面会进行讲解. 18 | 19 | 20 | 21 | #### 源码分析 22 | 23 | **category的五类迭代器以及继承关系** 24 | 25 | ```c++ 26 | struct input_iterator_tag {}; 27 | struct output_iterator_tag {}; 28 | struct forward_iterator_tag : public input_iterator_tag {}; 29 | struct bidirectional_iterator_tag : public forward_iterator_tag {}; 30 | struct random_access_iterator_tag : public bidirectional_iterator_tag {}; 31 | ``` 32 | 33 | 这五个类都是空类, 只是为了之后调用时通过类选择不同的重载函数. **继承是为了可以使用传递调用,当不存在某种迭代器类型匹配时编译器会依据继承层次向上查找进行传递**, 就可以通过继承关系来决定选择最优的调用. 我们通过用`distance`来讲最优. 34 | 35 | 36 | 37 | `distance`是用于计算连个迭代器之间的距离, 因为重载就可以通过不同的迭代器类型选择不同的函数来提高效率. 38 | 39 | 这里`distance`的`iterator_category`函数是每个迭代器自己定义的, 跟`traits`萃取器相关我准备放在下一篇章讲解. 这里只要知道它能通过`first`参数推断出是哪一类的迭代器从而选择调用哪一个函数. 40 | 41 | ```c++ 42 | template 43 | inline void distance(InputIterator first, InputIterator last, Distance& n) 44 | { 45 | __distance(first, last, n, iterator_category(first)); 46 | } 47 | 48 | template 49 | inline void __distance(InputIterator first, InputIterator last, Distance& n, 50 | input_iterator_tag) 51 | { 52 | while (first != last) 53 | { ++first; ++n; } 54 | } 55 | 56 | template 57 | inline void __distance(RandomAccessIterator first, RandomAccessIterator last, 58 | Distance& n, random_access_iterator_tag) 59 | { 60 | n += last - first; 61 | } 62 | ``` 63 | 64 | 从`distance`源码可以看出来不同的迭代器的计算方式并不一样, `random_access_iterator_tag`的距离的计算效率最高, 其他都是通过`++`操作来依次访问. 当然`random_access_iterator_tag`类的迭代器也是可以调用`input_iterator_tag`, 但是显然效率很低, 所以**不同的迭代器最自己最佳的效率**. 通过`iterator_category`进行最优选择. 65 | 66 | 67 | 68 | ### 五类迭代器源码 69 | 70 | 五类迭代器的结构体, 可以看出来每个类都定义了相同的变量名. 但是每个名的类型不一定一样, 提供统一的名是为了`traits`进行类型萃取. 每个类的`iterator_category`都是代表了不同的迭代器, 通过它来选择该迭代器执行的函数. 71 | 72 | ````c++ 73 | template struct input_iterator 74 | { 75 | typedef input_iterator_tag iterator_category; 76 | typedef T value_type; 77 | typedef Distance difference_type; 78 | typedef T* pointer; 79 | typedef T& reference; 80 | }; 81 | 82 | struct output_iterator { 83 | typedef output_iterator_tag iterator_category; 84 | typedef void value_type; 85 | typedef void difference_type; 86 | typedef void pointer; 87 | typedef void reference; 88 | }; 89 | 90 | template struct forward_iterator { 91 | typedef forward_iterator_tag iterator_category; 92 | typedef T value_type; 93 | typedef Distance difference_type; 94 | typedef T* pointer; 95 | typedef T& reference; 96 | }; 97 | 98 | 99 | template struct bidirectional_iterator { 100 | typedef bidirectional_iterator_tag iterator_category; 101 | typedef T value_type; 102 | typedef Distance difference_type; 103 | typedef T* pointer; 104 | typedef T& reference; 105 | }; 106 | 107 | template struct random_access_iterator { 108 | typedef random_access_iterator_tag iterator_category; 109 | typedef T value_type; 110 | typedef Distance difference_type; 111 | typedef T* pointer; 112 | typedef T& reference; 113 | }; 114 | ```` 115 | 116 | `iterator_category`判断传入迭代器的类型 117 | 118 | ```c++ 119 | template 120 | inline typename iterator_traits::iterator_category 121 | iterator_category(const Iterator&) { 122 | typedef typename iterator_traits::iterator_category category; 123 | return category(); 124 | } 125 | ``` 126 | 127 | ### 总结 128 | 129 | 这一篇仅仅只是讲解了一些关于迭代器类型, 和一点`traits`的一点用法. 关于每个迭代器都设置为相同的类型名都是为了`traits`萃取器做准备. 下篇进行探讨. 130 | 131 | -------------------------------------------------------------------------------- /6 模板中class与typename区别.md: -------------------------------------------------------------------------------- 1 | # typename与class 2 | 3 | ### 前言 4 | 5 | 在分析`traits`编程之前, 我们需要对模板参数类型`tempname`和`class`有一定的了解, 要明白他们在哪些方面不同, 哪些方面相同, 这样才能对体会到`traits`编程的核心. 如果你已经明白了两者, 那么你可以直接看下一篇了. 6 | 7 | 8 | 9 | ### 相同之处 10 | 11 | 一般对模板参数类型`typename`和`class`认为是一样的. 这两者在参数类型中确实是一样的. 你可以写成 12 | 13 | ```c++ 14 | template 15 | class point {}; 16 | ``` 17 | 18 | 也可以写成 19 | 20 | ```c++ 21 | template 22 | class point {}; 23 | ``` 24 | 25 | 这两者都是一样的, 没有区别. 两者`typename`和`class`在**参数类型**中没有不同 26 | 27 | *既然相同又为什么定义这两个符号呢?* 28 | 29 | 1. 最开始定义定义模板的方法就是template\ , 但是class毕竟都认为是一个类, 在使用时难免会有些点混淆, 也就定义了typename来标志参数类型 30 | 2. 最重要关于 typename可以使用嵌套依赖类型, 也就是类型可以嵌套使用. 这也是两个的不同之处. 31 | 32 | 33 | 34 | ### 不同之处 35 | 36 | `typename`可以用在嵌套依赖中, 并且表示其类型, 而`class`并没有这样的功能. 37 | 38 | *什么是嵌套依赖?* 我们以一个简单的实例来看 39 | 40 | ```c++ 41 | template 42 | class people 43 | { 44 | public: 45 | typedef T value_type; 46 | typedef T* pointer; 47 | typedef T& reference; 48 | }; 49 | 50 | template 51 | struct man 52 | { 53 | public: 54 | typedef typename T::value_type value_type; 55 | typedef typename T::pointer pointer; 56 | typedef typename T::reference reference; 57 | void print() 58 | { 59 | cout << "man" << endl; 60 | } 61 | }; 62 | 63 | int main() 64 | { 65 | man> Man; 66 | Man.print(); 67 | 68 | exit(0); 69 | } 70 | ``` 71 | 72 | 以上就是`typename`的嵌套使用. `typename`告诉编译器这不是一个函数, 也不是一个变量而是一个类型. 这里使用typedef又将参数类型重新定义一次, 1. 增加了一层间接性, 2. 使用的时候也不需要在写很长的代码. 73 | 74 | 这里`typename`是对people类中定义的类型进行了一次提取, 这里将`typename`改为`class`就会出错. 75 | 76 | `typename`主要的作用: 77 | 78 | - **对于模板参数是类的时候, `typename`能够提取出该类所定义的参数类型.** 79 | 80 | 并不是所有的嵌套依赖类型都要加上`typename`, 有一个例外 : **当继承列表或成员初始化列表中对基类进行初始化的时候, 可以去掉`typename`关键字** 81 | 82 | ```c++ 83 | man(int x) : T::value_type(x) {} 84 | ``` 85 | 86 | 87 | 88 | ### 总结 89 | 90 | 这里对`typename`做了一个浅显的分析, 这也足够我们可以分析`traits`编程的基础了. 我再将以上的分析做一个归纳. 91 | 92 | 1. `typename`和`class`在作为参数类型时用法一样, 没有区别 93 | 2. `typename`主要用于对嵌套依赖类型进行提取(萃取). 而`class`没有这样的功能. 94 | 3. `typename`提取的一个例外是在继承或成员初始化列表中对基类进行初始化时不用加`typename`关键字 -------------------------------------------------------------------------------- /7 traits萃取剂.md: -------------------------------------------------------------------------------- 1 | # traits萃取器 2 | 3 | ### 前言 4 | 5 | 前面我们分析了迭代器的五类, 而迭代器所指向对象的型别被称为`value type`. 传入参数的类型可以通过编译器自行推断出来, 但是如果是函数的返回值的话, 就无法通过`value type`让编译器自行推断出来了. 而`traits`就解决了函数返回值类型. 同样原生指针不能内嵌型别声明,所以内嵌型别在这里不适用, 迭代器无法表示原生指针(int *, char *等称为原生指针). 这个问题就通过`traits`偏特化技术解决的. 这一篇我们就主要探讨`traits`是怎么实现这些没有能解决的问题. 6 | 7 | 8 | 9 | ### iterator_traits结构 10 | 11 | `iterator_traits`结构体就是使用`typename`对参数类型的提取(萃取), 并且对参数类型在进行一次命名, 看上去对参数类型的使用有了一层间接性. 以下就是它的定义. 12 | 13 | ```c++ 14 | template 15 | struct iterator_traits { 16 | typedef typename Iterator::iterator_category iterator_category; //迭代器类型 17 | typedef typename Iterator::value_type value_type; // 迭代器所指对象的类型 18 | typedef typename Iterator::difference_type difference_type; // 两个迭代器之间的距离 19 | typedef typename Iterator::pointer pointer; // 迭代器所指对象的类型指针 20 | typedef typename Iterator::reference reference; // 迭代器所指对象的类型引用 21 | }; 22 | ``` 23 | 24 | 在五类迭代器对模板对象的类型重新定义一次. 这里提取(萃取)出来的参数类型名都是统一的, 也就说明每个要使用`traits`编程的类必须以此类型名为标准, 而且需要自己对类定义这些类型名. 25 | 26 | 上面的`traits`结构体并没有对原生指针做处理, 所以还要为特化, 偏特化版本(即原生指针)做统一. 以下便是iterator_traits 的特化和偏特化实现 27 | 28 | ```c++ 29 | // 针对原生指针 T* 生成的 traits 偏特化 30 | template 31 | struct iterator_traits { 32 | typedef random_access_iterator_tag iterator_category; 33 | typedef T value_type; 34 | typedef ptrdiff_t difference_type; 35 | typedef T* pointer; 36 | typedef T& reference; 37 | }; 38 | // 针对原生指针 const T* 生成的 traits 偏特化 39 | template 40 | struct iterator_traits { 41 | typedef random_access_iterator_tag iterator_category; 42 | typedef T value_type; 43 | typedef ptrdiff_t difference_type; 44 | typedef const T* pointer; 45 | typedef const T& reference; 46 | }; 47 | ``` 48 | 49 | 这样不管是函数返回值类型还是原生指针都能通过萃取器萃取出来, `typename I::类型`进行类型萃取. 50 | 51 | 52 | 53 | 前面也分析了一下`iterator_category`函数, 现在再来看一下就能明白, 该函数是通过`iterator_traits`萃取的类型的`iterrator_category`确定该迭代器的类型的, 五类迭代器都设置了不同的`iterator_category`的值, 最后调用`category()`函数确定传入参数的类型. 54 | 55 | ```c++ 56 | template 57 | inline typename iterator_traits::iterator_category 58 | iterator_category(const Iterator&) { 59 | typedef typename iterator_traits::iterator_category category; 60 | return category(); 61 | } 62 | // category的五类类型 63 | struct input_iterator_tag {}; 64 | struct output_iterator_tag {}; 65 | struct forward_iterator_tag : public input_iterator_tag {}; 66 | struct bidirectional_iterator_tag : public forward_iterator_tag {}; 67 | struct random_access_iterator_tag : public bidirectional_iterator_tag {}; 68 | ``` 69 | 70 | 继续看`distance`函数, `__distance`接受的前三个参数都是一样, 唯一不一样的就是最后一个参数, 通过`iterator_category`函数萃取出迭代器的类型从而根据类型而执行其对应的`__distance`函数. 71 | 72 | ```c++ 73 | // 根据第三个参数的类型调用相应的重载函数 74 | template 75 | inline void distance(InputIterator first, InputIterator last, Distance& n) 76 | { 77 | __distance(first, last, n, iterator_category(first)); 78 | } 79 | 80 | template 81 | inline void __distance(InputIterator first, InputIterator last, Distance& n, 82 | input_iterator_tag) 83 | {plate 84 | inline void __distance(InputIterator first, InputIterator last, Distance& n, 85 | input_iterator_tag) 86 | { 87 | while (first != 88 | while (first != last) 89 | { ++first; ++n; } 90 | } 91 | 92 | template 93 | inline void __distance(RandomAccessIterator first, RandomAccessIterator last, 94 | Distance& n, random_access_iterator_tag) 95 | { 96 | n += last - first; 97 | } 98 | ``` 99 | 100 | 这里又列出了两个类型的实现, **这里用到了0可以转换成指针的性质, 相当于返回一个空指针, 但是可以通过它们确定不同的参数类型.** 101 | ```c++ 102 | template 103 | inline typename iterator_traits::difference_type* 104 | distance_type(const Iterator&) { 105 | return static_cast::difference_type*>(0); 106 | } 107 | 108 | template 109 | inline typename iterator_traits::value_type* 110 | value_type(const Iterator&) { 111 | return static_cast::value_type*>(0); 112 | } 113 | ``` 114 | 115 | `value_type`在空间配置器的时有提过, 就是关于`destory`的第二个版本. 116 | 117 | ````c++ 118 | // 第二个版本的, 接受两个迭代器, 并设法找出元素的类型. 通过__type_trais<> 找出最佳措施 119 | template 120 | inline void destroy(ForwardIterator first, ForwardIterator last) 121 | { 122 | __destroy(first, last, value_type(first)); 123 | } 124 | ```` 125 | 126 | 127 | 128 | ### 总结 129 | 130 | `traits`编程使用`typename`和特化, 偏特化将迭代器没能支持原生指针, 不能推导出函数返回值的问题完善了. 同时`traits`编程技法对迭代器加以规范, 提前知道了对象的类型相关信息, 从而选择最优的函数执行, 少了类型转化, 提高了执行效率. 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /8 全特化和偏特化.md: -------------------------------------------------------------------------------- 1 | # 全特化和偏特化 2 | 3 | ### 前言 4 | 5 | 关于讲过`traits`萃取器的时候探讨到偏特化的概念, 而在那一篇文章也没有具体解释偏特化是什么, 怎么实现, 所以可能在第一次看得时候会很莫名其妙. 所以我将偏特化放在其后讲解, 为不明白的朋友做一个浅析的讲解. 这里我先聊一下全特化再聊偏特化. 6 | 7 | 8 | 9 | ### 全特化 10 | 11 | **全特化的模板参数列表应该是为空, 函数和类都可以实现全特化.** 12 | 13 | ```c++ 14 | template 15 | void fun(T a) 16 | { 17 | cout << "fun a = " << a << endl; 18 | } 19 | 20 | template<> 21 | void fun(int a) 22 | { 23 | cout << "fun1 a = " << a << endl; 24 | } 25 | 26 | int main() 27 | { 28 | fun(3.3); 29 | fun(3); 30 | 31 | exit(0); 32 | } 33 | ``` 34 | 35 | 结果如下 36 | 37 | ![特化1](assets/特化1.png) 38 | 39 | 这就是函数全特化, 根据传入的参数让编译器自动推导参数的类型来调用其特殊的函数. 40 | 41 | 记住: 42 | 43 | 1. **函数的全特化不是重载, 不是重载.** 44 | 2. **全特化的参数列表要为空, 为空** 45 | 3. 第二点成立是因为我们要实现一个相同的模板, 一个相同的模板 46 | 47 | 同样, 类的全特化也是一样的, 只要满足上面的三点就行了. 48 | 49 | 50 | 51 | ### 偏特化 52 | 53 | **函数不能偏特化, 类可以偏特化.** 54 | 55 | 偏特化需要在运行实例化的时候才能推导确定使用哪一个模板类. 偏特化也是以`template`来声明的,需要给出剩余的”模板形参”和必要的”模板实参”. 56 | 57 | ```c++ 58 | template 59 | class Point 60 | { 61 | public: 62 | void Print() 63 | { 64 | cout << "Point" << endl; 65 | } 66 | }; 67 | template 68 | class Point 69 | { 70 | public: 71 | void Print() 72 | { 73 | cout << "const Point" << endl; 74 | } 75 | }; 76 | int main() 77 | { 78 | Point b; 79 | b.Print(); 80 | Point c; 81 | c.Print(); 82 | 83 | exit(0); 84 | } 85 | ``` 86 | 87 | 88 | 89 | 以上就实现了一个关于`const T`的偏特化, 这就很像`traits萃取器`实现的偏特化了. 90 | 91 | ![特化2](assets/特化2.png) 92 | 93 | 偏特化的重点 : 94 | 95 | 1. 函数不能偏特化, 因为函数可以重载, 也就可以实现类型偏特化一样的功, 而类不可以重载. 96 | 2. 偏特化只是针对一些特殊的参数类型. 97 | 3. 偏特化实现了类的"重载". 98 | 99 | 还有**除了可以特化类模板之外, 还可以对类模板中的成员函数和普通静态成员变量进行特化**. 100 | 101 | 102 | 103 | ### 优先级 104 | 105 | 上面全特化和偏特化还有一点没有谈论到, 关于优先级. 现在我们就来看一下 106 | 107 | ```c++ 108 | // 这是上面的一个实例 109 | fun(3.3); 110 | fun(3); 111 | ``` 112 | 113 | fun(3)的函数不是调用`template void fun(T a)`而优先调用的是``的全特化模板. 114 | 115 | 同样偏特化的的例子也能证明这一点 116 | 117 | ```c++ 118 | Point b; 119 | b.Print(); 120 | Point c; 121 | c.Print(); 122 | ``` 123 | 124 | 类优先调用了最合适的模板. 125 | 126 | 上面就可以归纳为: 127 | 128 | 1. 全特化/偏特化, 普通模板(优先级从左到右依次减小) 129 | 130 | 131 | 132 | 现在我们再实现一个普通没有模板的fun函数 133 | 134 | ```c++ 135 | void fun(int a) {} 136 | ``` 137 | 138 | 如果继续调用`fun(3)`, 你会发现此时没有任何输出. 那是不是没有实现的模板函数和类的实例会优先被调用呢? 确实如此. 139 | 140 | 以上就可以归纳为 : 141 | 142 | 1. **全特化/偏特化, 普通模板(优先级从左到右依次减小)** 143 | 2. **无模板函数优先级最高** 144 | 145 | 146 | 147 | ### 总结 148 | 149 | **函数只能全特化, 不能偏特化, 类既可以全特化, 也可以偏特化.** 函数不能偏特化但是可以重载, 类不能进行重载. 150 | 151 | **优先级 : 无模板函数 > 全特化/偏特化 > 普通模板** 152 | 153 | 现在如果重新去看`traits萃取器`应该就能理解`traits`编程使用偏特化的意义. -------------------------------------------------------------------------------- /9 __type_traits型别.md: -------------------------------------------------------------------------------- 1 | # __type_traits型别 2 | 3 | ### 前言 4 | 5 | 上一篇探讨的是`traits`是为了将迭代器没能完善的原生指针, `traits`用特化和偏特化编程来完善. 这一篇准备探讨`__type_traits`, 为了将我们在空间配置器里面的提过的`__true_type`和`false_type`进行解答. 而`type_traits`型别对我们STL的效率又有什么影响, 有什么好处? 6 | 7 | ### __type_traits介绍 8 | 9 | 前面介绍的Traits技术在STL中弥补了C++模板的不足,但是Traits技术只是用来规范迭代器,对于迭代器之外的东西没有加以规范。因此,SGI将该技术扩展到迭代器之外,称为`__type_traits`。iterator_traits是萃取迭代器的特性,而__type_traits是萃取型别的特性。萃取的型别如下: 10 | 11 | - 是否具备non-trivial default ctor? 12 | - 是否具备non-trivial copy ctor? 13 | - 是否具备non-trivial assignment operator? 14 | - 是否具备non-trivial dtor? 15 | - 是否为POD(plain old data)型别? 16 | 17 | 其中non-trivial意指非默认的相应函数,编译器会为每个类构造以上四种默认的函数,如果没有定义自己的,就会用编译器默认函数,如果使用默认的函数,我们可以使用memcpy(),memmove(),malloc()等函数来加快速度,提高效率. 18 | 19 | 且`__iterator_traits`允许针对不同的型别属性在编译期间决定执行哪个重载函数而不是在运行时才处理, 这大大提升了运行效率. 这就需要STL提前做好选择的准备. 是否为**POD, non-trivial型别**用`__true_type`和`__false_type` 来区分. 20 | 21 | 22 | 23 | ### 空间配置器的例子 24 | 25 | 关于`__false_type`也在空间配置器提过. 现在再来看一看. 26 | 27 | ```c++ 28 | // 接受两个迭代器, 以__type_trais<> 判断是否有traival destructor 29 | template 30 | inline void __destroy(ForwardIterator first, ForwardIterator last, T*) 31 | { 32 | typedef typename __type_traits::has_trivial_destructor trivial_destructor; 33 | __destroy_aux(first, last, trivial_destructor()); 34 | } 35 | // non-travial destructor 36 | template 37 | inline void __destroy_aux(ForwardIterator first, ForwardIterator last, __false_type) 38 | { 39 | for ( ; first < last; ++first) 40 | destroy(&*first); 41 | } 42 | // travial destructor 43 | template 44 | inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type) {} 45 | ``` 46 | 47 | 通过函数`trivial_destructor()`确定型别来执行更高效的函数. 而且重载函数的选择实在编译时期就确定了. 马上就来探讨`__false_type`和`__true_type` 参数推导. 48 | 49 | 50 | 51 | ### 两个参数推导 52 | 53 | ```c++ 54 | struct __true_type {}; 55 | struct __false_type {}; 56 | ``` 57 | 58 | 我们不能将参数设为bool值, 因为需要在编译期就决定该使用哪个函数, 所以需要利用函数模板的参数推导机制, 将`__true_type`和`__false_type`表现为一个空类, 就不会带来额外的负担, 又能表示真假, 还能在编译时类型推导就确定执行相应的函数. 59 | 60 | 61 | 62 | ### __type_traits源码 63 | 64 | ```c++ 65 | __STL_TEMPLATE_NULL struct __type_traits { 66 | typedef __true_type has_trivial_default_constructor; 67 | typedef __true_type has_trivial_copy_constructor; 68 | typedef __true_type has_trivial_assignment_operator; 69 | typedef __true_type has_trivial_destructor; 70 | typedef __true_type is_POD_type; 71 | }; 72 | 73 | __STL_TEMPLATE_NULL struct __type_traits { 74 | typedef __true_type has_trivial_default_constructor; 75 | typedef __true_type has_trivial_copy_constructor; 76 | typedef __true_type has_trivial_assignment_operator; 77 | typedef __true_type has_trivial_destructor; 78 | typedef __true_type is_POD_type; 79 | }; 80 | 81 | ... 82 | ``` 83 | 84 | 以上是将基础的类型都设置为`__true_type`型别. 85 | 86 | ```c++ 87 | #ifdef __STL_CLASS_PARTIAL_SPECIALIZATION 88 | 89 | template 90 | struct __type_traits { 91 | typedef __true_type has_trivial_default_constructor; 92 | typedef __true_type has_trivial_copy_constructor; 93 | typedef __true_type has_trivial_assignment_operator; 94 | typedef __true_type has_trivial_destructor; 95 | typedef __true_type is_POD_type; 96 | }; 97 | 98 | #else /* __STL_CLASS_PARTIAL_SPECIALIZATION */ 99 | 100 | struct __type_traits { 101 | typedef __true_type has_trivial_default_constructor; 102 | typedef __true_type has_trivial_copy_constructor; 103 | typedef __true_type has_trivial_assignment_operator; 104 | typedef __true_type has_trivial_destructor; 105 | typedef __true_type is_POD_type; 106 | }; 107 | 108 | struct __type_traits { 109 | typedef __true_type has_trivial_default_constructor; 110 | typedef __true_type has_trivial_copy_constructor; 111 | typedef __true_type has_trivial_assignment_operator; 112 | typedef __true_type has_trivial_destructor; 113 | typedef __true_type is_POD_type; 114 | }; 115 | 116 | struct __type_traits { 117 | typedef __true_type has_trivial_default_constructor; 118 | typedef __true_type has_trivial_copy_constructor; 119 | typedef __true_type has_trivial_assignment_operator; 120 | typedef __true_type has_trivial_destructor; 121 | typedef __true_type is_POD_type; 122 | }; 123 | ``` 124 | 125 | 这里将指针进行特化处理, 同样是`__true_type`型别. 126 | 127 | SGI将所有的内嵌型别都定义为`false_type`, 这是对所有的定义最保守的值. 128 | 129 | ```c++ 130 | template 131 | struct __type_traits { 132 | typedef __true_type this_dummy_member_must_be_first; 133 | typedef __false_type has_trivial_default_constructor; 134 | typedef __false_type has_trivial_copy_constructor; 135 | typedef __false_type has_trivial_assignment_operator; 136 | typedef __false_type has_trivial_destructor; 137 | typedef __false_type is_POD_type; 138 | }; 139 | ``` 140 | 141 | 从上面的源码可以明白, 所有的基本类型都是设置的为`__true_type`型别, 而所有的对象都会被特化为`__false_type`型别, 这是为了保守而不得这样做. 也因为`__true_type`型别让我们在执行普通类型时能够以最大效率进行对其的copy, 析构等操作. 142 | 143 | 144 | 145 | ### 总结 146 | 147 | SGI对`traits`进行扩展,使得所有类型都满足`traits`编程规范, 这样SGI STL算法可以通过`__type_traits`获取类型信息在编译期间就能决定出使用哪一个重载函数, 解决了`template`是在运行时决定重载选择的问题. 并且通过`true`和`false`来确定POD和travial destructor, 让程序能选择更加符合其类型的处理函数, 大大提高了对基本类型的快速处理能力并保证了效率最高. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SIG STL源码分析 2 | 3 | ## 前言 4 | 5 | 本专栏主要以STL源码剖析分析路线来分析SIGSTL3.0源码. 6 | 7 | 整个模块准备对学习`STL源码剖析`之后做一个系统的总结, 这些都是我个人的理解, 如果分析有什么问题欢迎各位大佬们指出. 也很感谢作者以及网络中各个大佬的总结, 让我也能更容易更深刻的理解到`STL`强大和方便, 也让我对`template`感受深刻. 8 | 9 | 以下是我自己对STL版块进行分析. 10 | 11 | **总共分为六个版块 : 空间配置器, 迭代器, 容器(序列容器, 关联容器), 算法, 仿函数, 配接器.** 12 | 13 | 14 | 15 | ## STL前期准备 16 | 17 | 在学习STL源码之前需要对`template`有一个认识或者回忆. 18 | 19 | [template(一)](https://github.com/FunctionDou/STL/blob/master/template%E4%B9%8B%E6%A8%A1%E6%9D%BF%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md) 20 | 21 | [template(二)](https://github.com/FunctionDou/STL/blob/master/template%E4%B9%8B%E9%9D%9E%E7%B1%BB%E5%9E%8B%E6%A8%A1%E6%9D%BF%E5%8F%82%E6%95%B0.md) 22 | 23 | [template(三)](https://github.com/FunctionDou/STL/blob/master/template%E4%B9%8B%E7%B1%BB%E7%9B%B8%E5%85%B3.md) 24 | 25 | ## STL分析 26 | 27 | 28 | 29 | ### 空间配置器 30 | 31 | c/c++都需要手动的管理内存, 而封装实现一个能申请空间又能自己释放空间不再让我们自己管理. 而STL就实现了这样的一个功能, 它就是`空间配置器`. 而空间配置器一般是隐藏在各个版块的组件, 实现中我们都看不到它的存在, 但是它确实是非常重要的部分, 因为它, 所有的版块操作内存时都直接调用它就行了, 而不需要再实现内存的分配. 32 | 33 | [0. new实现](https://blog.csdn.net/Function_Dou/article/details/84526761) 34 | 35 | [1. 空间配置器](https://github.com/FunctionDou/STL/blob/master/1%20%E5%88%9D%E6%AC%A1%E6%8E%A5%E8%A7%A6%E7%A9%BA%E9%97%B4%E9%85%8D%E7%BD%AE%E5%99%A8.md) 36 | 37 | [2. 第一级配置器](https://github.com/FunctionDou/STL/blob/master/2%20第一级配置器.md) 38 | 39 | [3. 第二级配置器](https://github.com/FunctionDou/STL/blob/master/3%20%E7%AC%AC%E4%BA%8C%E7%BA%A7%E9%85%8D%E7%BD%AE%E5%99%A8.md) 40 | 41 | [4. 内存池](https://github.com/FunctionDou/STL/blob/master/4%20%E5%86%85%E5%AD%98%E6%B1%A0.md) 42 | 43 | 44 | 45 | ### 迭代器 46 | 47 | 每个组件都可能会涉及到对元素的简单操作, 比如 : 访问, 自增, 自减等. 每个组件都的数据类型可能不同, 所以每个组件可能都要自己设计对自己的操作. 但将每个组件的实现成统一的接口就是迭代器, 48 | 49 | 它的优点也很明显: 50 | 51 | 1. 它是能屏蔽掉底层数据类型差异的. 52 | 2. 迭代器将容器和算法粘合在一起, 使版块之间更加的紧凑, 同时提高了执行效率, 让算法更加的得到优化. 53 | 54 | 这些实现大都通过`traits`编程实现的. 它的定义了一个类型名规则, 满足`traits`编程规则就可以自己实现对`STL`的扩展, 也体现了`STL`的灵活性. 同时`straits`编程让程序根据不同的参数类型选择执行更加合适参数类型的处理函数, 也就提高了`STL`的执行效率. 可见迭代器对`STL`的重要性. 55 | 56 | [1. 迭代器](https://github.com/FunctionDou/STL/blob/master/5%20%E8%BF%AD%E4%BB%A3%E5%99%A8.md) 57 | 58 | [2. template(四)](https://github.com/FunctionDou/STL/blob/master/6%20%E6%A8%A1%E6%9D%BF%E4%B8%ADclass%E4%B8%8Etypename%E5%8C%BA%E5%88%AB.md) 59 | 60 | [3. traits萃取剂](https://github.com/FunctionDou/STL/blob/master/7%20traits%E8%90%83%E5%8F%96%E5%89%82.md) 61 | 62 | [4. template(五)](https://github.com/FunctionDou/STL/blob/master/8%20%E5%85%A8%E7%89%B9%E5%8C%96%E5%92%8C%E5%81%8F%E7%89%B9%E5%8C%96.md) 63 | 64 | [5. __type_traits型别](https://github.com/FunctionDou/STL/blob/master/9%20__type_traits%E5%9E%8B%E5%88%AB.md) 65 | 66 | 67 | 68 | ### 容器 69 | 70 | 容器是封装了大量常用的数据结构, 因为容器, 凸显出STL的方便, 操作简单. 毕竟它将常用的但是实现比较麻烦的数据结构封装之后就可以直接的调用, 不再让用户重写一长串的代码实现. 71 | 72 | 容器根据排列分为了**序列式和关联式**. 73 | 74 | 1. 序列式包括`vector`, `list`, `deque`等. 序列容器有头或尾, 甚至有头有尾. 75 | 2. 关联式包括`map`, `set`, `hashtable`等. 关联容器没有所谓的头尾, 只有最大值, 最小值. 76 | 77 | 学习容器的时候要注意`end`返回的是**最后一元素的后一个地址, 这个地址并没有储存实际的值.** 78 | 79 | [0. uninitialized系列函数](https://github.com/FunctionDou/STL/blob/master/10%20uninitialized.md) 80 | 81 | ##### 序列容器 82 | 83 | [1. vector序列容器(一)](https://github.com/FunctionDou/STL/blob/master/11%20vector%20%E4%B8%8A.md) 84 | 85 | [2. vector序列容器(二)](https://github.com/FunctionDou/STL/blob/master/12%20vector%20%E4%B8%AD.md) 86 | 87 | [3. vector序列容器(三)](https://github.com/FunctionDou/STL/blob/master/13%20vector%20%E4%B8%8B.md) 88 | 89 | [4. list有序容器(一)](https://github.com/FunctionDou/STL/blob/master/14%20list%20%E4%B8%8A.md) 90 | 91 | [5. list有序容器(二)](https://github.com/FunctionDou/STL/blob/master/15%20list%20%E4%B8%AD.md) 92 | 93 | [6. list有序容器(三)](https://github.com/FunctionDou/STL/blob/master/16%20list%20%E4%B8%8B.md) 94 | 95 | [7. deque有序容器(一)](https://github.com/FunctionDou/STL/blob/master/17%20deque%20%E4%B8%8A.md) 96 | 97 | [8. deque有序容器(二)](https://github.com/FunctionDou/STL/blob/master/18%20deque%20%E4%B8%AD.md) 98 | 99 | [9. deque有序容器(三)](https://github.com/FunctionDou/STL/blob/master/19%20deque%20%E4%B8%8B.md) 100 | 101 | [10. stack配接器](https://github.com/FunctionDou/STL/blob/master/20%20stack.md) 102 | 103 | [11. queue配接器](https://github.com/FunctionDou/STL/blob/master/21%20queue.md) 104 | 105 | [12. heap大根堆](https://github.com/FunctionDou/STL/blob/master/22%20heap.md) 106 | 107 | [13. 优先级队列](https://github.com/FunctionDou/STL/blob/master/23%20priority_queue.md) 108 | 109 | [14. slist有序容器(一)](https://github.com/FunctionDou/STL/blob/master/24%20slist%20%E4%B8%8A.md) 110 | 111 | [15. slist有序容器(二)](https://github.com/FunctionDou/STL/blob/master/25%20slist%20下.md) 112 | 113 | 114 | 115 | ##### 关联容器 116 | 117 | [1. RB-tree关联容器(一)](https://github.com/FunctionDou/STL/blob/master/26%20RB-tree%20%E4%B8%8A.md) 118 | 119 | [2. RB-tree关联容器(二)](https://github.com/FunctionDou/STL/blob/master/27%20RB-tree%20%E4%B8%8B.md) 120 | 121 | [3. set配接器](https://github.com/FunctionDou/STL/blob/master/28%20set.md) 122 | 123 | [4. pair结构体](https://github.com/FunctionDou/STL/blob/master/29%20pair.md) 124 | 125 | [5. map配接器](https://github.com/FunctionDou/STL/blob/master/30%20map.md) 126 | 127 | [6. multiset配接器](https://github.com/FunctionDou/STL/blob/master/31%20multiset.md) 128 | 129 | [7. multimap配接器](https://github.com/FunctionDou/STL/blob/master/32%20multimap.md) 130 | 131 | [8. hashtable关联容器(一)](https://github.com/FunctionDou/STL/blob/master/33%20hashtable%20%E4%B8%8A.md) 132 | 133 | [9. hashtable关联容器(二)](https://github.com/FunctionDou/STL/blob/master/34%20hashtable%20%E4%B8%8B.md) 134 | 135 | [10. hash_set配接器](https://github.com/FunctionDou/STL/blob/master/35%20hash_set.md) 136 | 137 | [11. hash_multiset配接器](https://github.com/FunctionDou/STL/blob/master/36%20hash_multiset.md) 138 | 139 | [12. hash_map配接器](https://github.com/FunctionDou/STL/blob/master/37%20hash_map.md) 140 | 141 | [13. hash_multimap配接器](https://github.com/FunctionDou/STL/blob/master/38%20hash_multimap.md) 142 | 143 | 144 | 145 | ### 算法 146 | 147 | STL的算法有很多, 有简单也有一些复杂的, 这里我就以`STL3.0`源码为例, 挑选出来几个常用的算法来进行分析. 148 | 149 | [1. copy算法](https://github.com/FunctionDou/STL/blob/master/39%20%E7%AE%97%E6%B3%95--copy.md) 150 | 151 | ### 仿函数 152 | 153 | 所谓仿函数也就是函数对象, 以前是这样称呼它的, 只是一直沿用至今了. **仿函数就是一种具有函数特质的对象**. STL因为仿函数, 大大在增加了灵活性, 而且可以将部分操作由用户自己来定义然后传入自定义的函数名就可以被调用. 但是你会发现仿函数实现的功能都是相当简单的, 而且都要通过配接器再封装才能够使用. 154 | 155 | STL的仿函数根据参数个数可以分为 : 一元仿函数和二元仿函数. 根据功能可分为 : 算术运算, 关系运算和逻辑运算. 156 | 157 | [1. 仿函数](https://github.com/FunctionDou/STL/blob/master/44%20%E4%BB%BF%E5%87%BD%E6%95%B0.md) 158 | 159 | 160 | 161 | ### 配接器 162 | 163 | 主要用来修改接口. 修改迭代器的接口, 修改容器的接口, 修改仿函数的接口. 164 | 165 | [1. 配接器](https://github.com/FunctionDou/STL/blob/master/45%20%E9%85%8D%E6%8E%A5%E5%99%A8.md) 166 | 167 | 168 | 169 | ## 总结 170 | 171 | STL这本书对我这个菜鸟收获还是挺有帮助的, 了解了`template`的强大, STL对程序做到了最大的优化. STL对每个功能都尽可能的单一, 然后可能通过多次的函数调用才调用真正执行的函数. 172 | 173 | 让我对c++有了一个更深的认识, 真的很难, 自己还差的太远, 努力加油! 174 | -------------------------------------------------------------------------------- /assets/特化1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CofeCore/STL/c2196f284cbe387eebc092037c5d2abcc97a0566/assets/特化1.png -------------------------------------------------------------------------------- /assets/特化2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CofeCore/STL/c2196f284cbe387eebc092037c5d2abcc97a0566/assets/特化2.png -------------------------------------------------------------------------------- /template 4.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CofeCore/STL/c2196f284cbe387eebc092037c5d2abcc97a0566/template 4.md -------------------------------------------------------------------------------- /template之模板注意事项.md: -------------------------------------------------------------------------------- 1 | # template之模板注意事项 2 | 3 | ## 前言 4 | 5 | 在分析`STL`之前, 我们需要先对`template`做一个回忆, 可能我总结的内容你都会了, 也可能你没有了印象了, 但是我还是希望你先浏览一下template的用法. 毕竟STL全部都涉及到了模板, 而template是学习STL的基础. 我将template暂时分成5节来讲, 这里就先分析三节, 后面.两节准备放在STL中来分析. 6 | 7 | 8 | 9 | ## template使用 10 | 11 | `template`的使用大大提高了代码的复用性, 抽象性. 12 | 13 | 1. **类模板实例化时并不是每个成员函数都实例化了, 而是使用到了哪个成员函数, 那个成员函数才实例化**. 14 | 15 | ```c++ 16 | /* ***** 1 *******/ 17 | template 18 | class point 19 | { 20 | public: 21 | point() : x(0), y(0) {} 22 | point(T x, T y) : x(x), y(y) {} 23 | T getX() const { x = y; return x; } // 一般是无法通过编译的, x不允许被修改, 但是这里并没有报错 24 | private: 25 | T x; 26 | T y; 27 | }; 28 | /* ***** 2 *******/ 29 | #define T int 30 | class point 31 | { 32 | public: 33 | point() : x(0), y(0) {} 34 | point(T x, T y) : x(x), y(y) {} 35 | T getX() const { x = y; return x; } 36 | private: 37 | T x; T y; 38 | }; 39 | ``` 40 | 41 | 成员函数getX()应该无法通过编译, 就像实例2一样, 但是因为模板中没有使用到函数getX(), 也就没有实例化getX, 所以就没有出现编译报错. 实例2必须在编译的时候就要检查所有的成员即函数. 42 | 43 | **不是所有的模板都只能在运行时才会被实例化, 比如非类型模板参数就能在编译时期就已经实例化了, 这里主要说的是类模板, 不要混淆了.** 44 | 45 | 2. **可以把类模板和函数模板结合起来, 定义一个含有成员函数模板的类模板**. 46 | 47 | ```c++ 48 | template 49 | class point 50 | { 51 | public: 52 | point() : x(0), y(0) {} 53 | point(T x, T y) : x(x), y(y) {} 54 | template // 定义了另一个的模板参数类型 55 | void print(U x); 56 | private: 57 | T x; 58 | T y; 59 | }; 60 | 61 | // 这里两个都要写出来 62 | template 63 | template 64 | void point::print(U x) 65 | { 66 | std::cout << this->x + x; 67 | } 68 | 69 | int main() 70 | { 71 | point p; 72 | p.print(3.14); // 因为是模板函数, 所以交给编译器自行推导 73 | 74 | exit(0); 75 | } 76 | ``` 77 | 78 | 79 | 80 | 3. **类模板中可以声明static成员, 在类外定义的时候要增加template相关的关键词, 并且需要注意每个不同的模板实例都会有一个独有的static成员对象**. 81 | 82 | ```c++ 83 | template 84 | class tmp 85 | { 86 | public: 87 | static T t; 88 | }; 89 | 90 | template 91 | T tmp::t = 0; 92 | 93 | int main() 94 | { 95 | tmp t1; 96 | tmp t2; 97 | tmp t3; 98 | t1.t = 1; 99 | std::cout << "t1.t = " << t1.t << endl; 100 | std::cout << "t2.t = " << t2.t << endl; 101 | cout << "t3.t = " << t3.t << endl; 102 | 103 | exit(0); 104 | } 105 | ``` 106 | 107 | 输出结果: 108 | 109 | ```c++ 110 | t1.t = 1 111 | t2.t = 1 112 | t3.t = 0 113 | ``` 114 | 115 | **模板中的`static`是在每个不同的类型实例化一个, 相同类型的实例化对象共享同一个参数.** 所以这里的t1, t2中哦t都是同一个实例化变量, 是共享的. 116 | 117 | 118 | 119 | ## 总结 120 | 121 | 本节只是简单讲了`template`在实例化时期和使用static, 嵌套模板的使用和注意, 下一节着重分析template的非类型参数. 122 | -------------------------------------------------------------------------------- /template之类相关.md: -------------------------------------------------------------------------------- 1 | # template之类相关 2 | 3 | ## 前言 4 | 5 | 前面两节主要分析了使用`template`的注意点以及非类型参数如何使用, 本节来探讨虚模板函数和模板拷贝构造函数. 6 | 7 | 8 | 9 | ## 虚函数模板 10 | 11 | 在我们使用模板从来都没有将虚函数与模板进行套用, 那么这两者能不能同时连用呢? 12 | 13 | 这个直接来写代码验证才知道. 14 | 15 | ```c++ 16 | class point 17 | { 18 | public: 19 | template 20 | virtual T getX() 21 | { 22 | return x; 23 | } 24 | private: 25 | int x; 26 | }; 27 | 28 | int main() 29 | { 30 | exit(0); 31 | } 32 | ``` 33 | 34 | 分别用了`g++`和`clang`编译 35 | 36 | ```c++ 37 | // g++ 38 | virtual_template.cpp:17:4: error: templates may not be ‘virtual’ 39 | virtual T getX() 40 | // clang 41 | virtual_template.cpp:17:4: error: 'virtual' cannot be specified on member function templates 42 | virtual T getX() 43 | ``` 44 | 45 | 可以看出来`clang`更加准确的指出了`virtual`不能是member function templates. 46 | 47 | *为什么虚函数不能是模板函数?* 48 | 49 | - 编译器在编译类的定义的时候就必须确定该类的虚表大小. 50 | - 模板只有在运行调用时才能确认其大小, 两者冲突. 结果显而易见. 51 | 52 | 53 | 54 | ## 模板拷贝构造函数 55 | 56 | 模板与不同模板之间不能直接的赋值(强制转换), 毕竟模板一般都是类和函数都不是普通的类型. 但是类有拷贝构造函数, 所以我们可以对类的构造函数进行修改, 也就成了模板构造函数. 57 | 58 | 模板拷贝构造函数与一般的拷贝构造函数没有什么区别, 仅仅实在加了一个模板作为返回值类型 59 | 60 | ```c++ 61 | template 62 | class tmp 63 | { 64 | public: 65 | tmp() : x(0) {} 66 | template 67 | tmp(const tmp& t) 68 | { 69 | x = t.x; 70 | } 71 | private: 72 | T x; 73 | }; 74 | 75 | int main() 76 | { 77 | tmp t1; 78 | tmp t2; 79 | t1 = t2; 80 | 81 | exit(0); 82 | } 83 | ``` 84 | 85 | 86 | 87 | ## 总结 88 | 89 | 本节分析了类的虚表大小要在编译时知道其大小所以虚函数不能为模板函数, 模板构造函数与普通的构造函数写法无意, 并且可以实现强制转换的效果. 90 | 91 | -------------------------------------------------------------------------------- /template之非类型模板参数.md: -------------------------------------------------------------------------------- 1 | # template之非类型模板参数 2 | 3 | ## 前言 4 | 5 | 前一节分析了关于`template`的使用注意, 本节分析关于`template`非类型参数的使用, 非类型参数可能在有些人认为并没有太大作用, 但是既然C++规定有能这样使用就肯定有其意义, 这里就做一个浅析. 6 | 7 | 8 | 9 | ## 非类型模板参数 10 | 11 | **非类型参数, 可用在模板中自定义为整型类型, 指针或引用, 不能定义为浮点数等其他类型.** 12 | 13 | 非类型模板参数在编译期间就已经实例化, 所以其模板实参必须是常量表达式. 14 | 15 | ```c++ 16 | template; // N是编译时就确定的常量表达式 17 | template; // N,M是编译时就确定的常量表达式 18 | ``` 19 | 20 | 可能就是会觉得没有用, 毕竟使用模板就是要用他的模板参数类型啊, 没有这个那还怎么用. 这里就来先看一个例子. 21 | 22 | *要求: 实现一个函数返回一个数组的真实大小, 如 : int a[100]; ArrSize(a);返回100* 23 | 24 | 嗯? 讲道理传入函数中a就转换为指针了, 怎么用指针能获取其表示范围? 这里就要用到`template`的非类型参数. 25 | 26 | ```c++ 27 | template // 这里的N是编译时期就知道了, 所以可以加上constexpr关键字 28 | constexpr std::size_t ArrSize(T (&a)[N]) noexcept 29 | { 30 | return N; 31 | } 32 | int a[100]; ArrSize(a); 33 | ``` 34 | 35 | 实现了这个功能后我们就来分析一下. 36 | 37 | 函数模板通过传入a后会自动推导出 T 的类型为 int, N 的大小为 100, 函数通过引用, 所以传入的是一个a而不是一个指针. 38 | 39 | *重点在于模板参数N能自动推导传入数组的大小*. 40 | 41 | 42 | 43 | 同样我们可以将`strcmp`做一个封装, 实现一个字符串比较的模板函数. 44 | 45 | ```c++ 46 | template 47 | bool compare(const char (&a)[N], const char (&b)[M]) 48 | { 49 | return strcmp(a, b); 50 | } 51 | ``` 52 | 53 | 54 | 55 | ## 总结 56 | 57 | 使用`template`的非类型参数可以自动帮我们获取参数的大小, 省去手动传入参数大小的麻烦等问题. 记住 : 非类型模板参数在编译期间就已经实例化, 所以其模板实参必须是常量表达式. 58 | --------------------------------------------------------------------------------