diff --git "a/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_SGI\347\211\210\346\234\254\347\251\272\351\227\264\351\205\215\347\275\256\345\231\250.md" "b/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_SGI\347\211\210\346\234\254\347\251\272\351\227\264\351\205\215\347\275\256\345\231\250.md" new file mode 100644 index 0000000..9e98929 --- /dev/null +++ "b/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_SGI\347\211\210\346\234\254\347\251\272\351\227\264\351\205\215\347\275\256\345\231\250.md" @@ -0,0 +1,623 @@ +- [一、STL中的空间配置器](#一stl中的空间配置器) +- [二、一级空间配置器](#二一级空间配置器) + - [2.1、为内存不足做好准备](#21为内存不足做好准备) + - [2.2、抽取的代码实现](#22抽取的代码实现) + - [2.3、一级空间配置器总结](#23一级空间配置器总结) +- [三、二级空间配置器](#三二级空间配置器) +- [四、完整代码、测试代码、测试结果](#四完整代码测试代码测试结果) + - [4.1、完整代码](#41完整代码) + - [4.2、测试代码](#42测试代码) + - [4.3、测试结果](#43测试结果) +- [五、STL中的不成文规定](#五stl中的不成文规定) +- [六、说明](#六说明) + +## 一、STL中的空间配置器 + +在STL中,空间配置器分了2组,分别为一级空间配置器和二级空间配置器,但是它们都有自己各自运用的场合;一般说来,一级空间配置器一般分配的空间大于128B,二级空间配置器的分配空间为小于128B。 + +其中SGI 中的STL的空间配置器设计哲学如下: + +1. 向system heap要求申请空间 +2. 考虑多线程的状态 +3. 考虑内存不足时的应变措施 +4. 考虑过多"小型区块"可能造成的内存碎片问题 + +**在剖析源码时,为了将问题控制在一定的复杂度内,以下源码皆排除多线程状态的处理;** + +- 0:表示非多线程版本; +- inst : 表示不关注多线程的问题; + +## 二、一级空间配置器 + +### 2.1、为内存不足做好准备 + +- 抛出异常,也就是输出一句话; +- 调用自己设置的函数去处理(比如说是释放一些空间,回收一些碎片的空间); + +**一级空间配置器的本质:模仿实现了set_new_handler机制;** + +set_new_handler机制的实现: + +1. 定义一个函数指针; +2. 定义一个函数; +3. 赋值比较; + +### 2.2、抽取的代码实现 + +```cpp +#if 1 +#include +#include +#include +using namespace std; +//#define __THROW_BAD_ALLOC throw bad_alloc +#define __THROW_BAD_ALLOC cerr<<"Throw bad alloc, Out Of Memory."< +#define __THROW_BAD_ALLOC cerr<<"out of memory"< +class __malloc_alloc_template{ +private: + static void* oom_malloc(size_t); //对申请空间失败的处理函数 + static void* oom_realloc(void *, size_t); //对扩展空间失败处理的函数 + static void(* __malloc_alloc_oom_handler)(); //定义一个函数指针 + +public: + static void* allocate(size_t n){ //分配空间 + void *result = malloc(n); + if(0 == result) + result = oom_malloc(n); //分配空间失败,调用oom_malloc()函数 + return result; //将申请的空间的地址返回 + } + static void deallocate(void *p, size_t){ + free(p); //释放空间 + } + static void* reallocate(void *p, size_t, size_t new_sz){ + void *result = realloc(p, new_sz); //扩展新空间; + if(0 == result) //扩展失败 + oom_realloc(p,new_sz); //调用扩展空间失败的处理函数 + return result; + } +public: + //set_new_handler(Out_Of_Memory); + static void(*set_malloc_handler(void(*f)()))(){ //这是一个指针函数,函数名称:set_malloc_handler,参数:是一个函数指针,返回值:是一个函数指针; + void(*old)() = __malloc_alloc_oom_handler; //将原有空间的地址保存在old中; + __malloc_alloc_oom_handler = f; //将自己定义的函数地址给__malloc_alloc_oom_handler; + return old; //每次可以保存其上一次的地址. + } +}; + +template +void (*__malloc_alloc_template::__malloc_alloc_oom_handler)() = 0; //对定义的静态函数指针初始化为0; + +template +void* __malloc_alloc_template::oom_malloc(size_t n){ //处理空间失败的问题 + void *result; + void(* my_malloc_handler)(); //定义一个函数指针; + + for(;;){ + my_malloc_handler = __malloc_alloc_oom_handler; + if(0 == my_malloc_handler){ //自己没有定义处理空间失败的新函数 + __THROW_BAD_ALLOC; //异常抛出;程序终止; + } + (*my_malloc_handler)(); //调用自己编写的处理函数(一般都是回收空间之类的); + result = malloc(n); //在此申请分配空间 + if(result){ //申请成功 + return result; //将地址返回; + }//那么,这个程序将会一直持续到空间分配成功才最终返回. + } +} + + +template +void* __malloc_alloc_template::oom_realloc(void *p, size_t n){ + void(*my_malloc_handler)(); //函数指针 + void *result; + for(;;){ + my_malloc_handler = __malloc_alloc_oom_handler; //将这个给其赋值 + if(0 == my_malloc_handler){ //外面没有定义处理的函数 + __THROW_BAD_ALLOC; //异常抛出,程序结束. + } + (*my_malloc_handler)(); //调用自己编写的处理函数(一般都是回收空间之类的); + result = realloc(p, n); //再次扩展空间分配; + if(result){ //扩展成功,就返回 + return result; + } //一直持续成功,知道扩展空间分配成功才返回; + } +} + +typedef __malloc_alloc_template<0> malloc_alloc; //一级空间配置器:malloc_alloc; +``` + +第一级空间配置器就是: + +1. 对malloc、free的简单封装; +2. 模拟C++的set_new_handler()已处理内存不足的情况; + +### 2.3、一级空间配置器总结 + +1. **一级空间配置器其实就是先自己开辟空间,要是失败了;** +2. **调用处理空间失败的函数,在其内部,先看我们自己在外面有没有写针对这个的处理函数,要是没写,就是异常抛出,程序结束;** +3. **要是写了,就调用我们自己写的函数,在次分配空间,进入死循环中,直到空间分配成功,方可退出循环;** + +还要注意的是: + +```cpp +static void(*set_malloc_handler(void(*f)()))();这是一个指针函数; +``` + +## 三、二级空间配置器 + +1、当所分配的空间小于128B时,则以内存池去管理 ; 对小额区块,自动将内存调至8的倍数,并维护16个自由链表,各自管理大小分别为: 8 16 24 32 40 48 56 ....128B的小额区块; + +2、刚开始所分配的内存空间,一半当做自由链表,一半当做内存池;当再次分配同样大小的空间时,直接先从自由链表中分配;当再次分配其他大小空间时,先看内存池中有无空间,有的话,直接分配,挂载即可。 + +模型如下: + +
+ +整个分配空间都是很节省化的: + +其抽取代码如下: + +```cpp +//二级空间配置器由自由链表和内存池组成; +enum {__ALIGN = 8}; //一块链表8B +enum {__MAX_BYTES = 128}; //小于128B调用二级的 +enum {__NFREELISTS = __MAX_BYTES / __ALIGN}; //一共分配16个自由链表,负责16种次分配能力. + +template //不考虑多线程状态; +class __default_alloc_template{ +public: + static void* allocate(size_t n); //分配空间 + static void deallocate(void *p, size_t n); //销毁空间 + static void* reallocate(void *p, size_t, size_t new_sz); //扩展空间 +private: + static size_t ROUND_UP(size_t bytes){ //向上调整函数; + return (((bytes) + __ALIGN-1) & ~(__ALIGN-1)); //调为当前字节是8的整数倍. + } +private: + union obj{ //共用体 + union obj * free_list_link; //自由链表的指向 + char client_data[1]; + }; +private: + static obj* volatile free_list[__NFREELISTS]; //定义了一个指针数组; + static size_t FREELIST_INDEX(size_t bytes){ //求当前字节的自由链表的下标; + return ((bytes)+__ALIGN-1) / __ALIGN-1; + } +private: + static char *start_free; //开始空间的下标 + static char *end_free; //结束空间的下标 + static size_t heap_size; //堆空间大小 + static void *refill(size_t n); //填充函数 + static char* chunk_alloc(size_t size, int &nobjs); // +}; + + +template //以下都是对静态变量的初始化,都为0; +char* __default_alloc_template::start_free = 0; +template +char* __default_alloc_template::end_free = 0; +template +size_t __default_alloc_template::heap_size = 0; +template +typename __default_alloc_template::obj* volatile +__default_alloc_template::free_list[__NFREELISTS] = +{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +template +void* __default_alloc_template::allocate(size_t n){ //分配空间的函数 + obj * volatile *my_free_list; + obj *result; + + if(n > __MAX_BYTES){ //分配空间的大小大于128B的话,就调用一级空间配置器 + return malloc_alloc::allocate(n); + } + + my_free_list = free_list + FREELIST_INDEX(n); //free_list是二维数组名称,找往哪个链下挂; + result = *my_free_list; //取出其值,因为my_free_list是二阶指针; + + if(result == 0){ //没有内存池空间; + void *r = refill(ROUND_UP(n)); //调用refill()函数 + return r; + } + + *my_free_list = result->free_list_link; //进行挂载连接; + return result; +} + +template +void* __default_alloc_template::refill(size_t n){ //没有可用区块时,就调用refill()函数; + int nobjs = 20;//就是要分20块; + char *chunk = chunk_alloc(n, nobjs); //调用内存池函数; + obj * volatile *my_free_list; + + obj *result; + obj *current_obj, *next_obj; + int i; + + if(1 == nobjs){ //当分配到只有一块空间时,直接返回. + return chunk; + } + + my_free_list = free_list + FREELIST_INDEX(n); //找到对应的下标,进行连接的工作; + result = (obj*)chunk; + *my_free_list = next_obj = (obj*)(chunk+n); + + for(i=1; ; ++i){ + current_obj = next_obj; + next_obj = (obj*)((char*)next_obj+n); + if(nobjs - 1 == i){ //进行连接工作; + current_obj->free_list_link = 0; + break; + }else{ + current_obj->free_list_link = next_obj; + } + } + return result; +} + +template +char* __default_alloc_template::chunk_alloc(size_t size, int &nobjs){ //内存池函数 + char *result; //关键要明白以下的各种情况; + size_t total_bytes = size * nobjs; //这里的size已经是上调过的字节;求取20*size个字节的大小 + size_t bytes_left = end_free - start_free; //刚开始,遗留字节为0; + if(bytes_left >= total_bytes){ //不成立 + result = start_free; + start_free += total_bytes; + return result; + }else if(bytes_left >= size){ //不成立 + nobjs = bytes_left / size; + total_bytes = size * nobjs; + result = start_free; + start_free += total_bytes; + return result; + }else{ //走的就是下面的这条路线 + size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4); //申请2倍的total_bytes; + if(bytes_left > 0){ //遗留字节数=0,所以这条语句不成立; + obj * volatile * my_free_list = free_list + FREELIST_INDEX(bytes_left); + ((obj*)start_free)->free_list_link = *my_free_list; + *my_free_list = (obj *)start_free; + } + + start_free = (char *)malloc(bytes_to_get); //申请空间; + if(0 == start_free){ + int i; + obj * volatile *my_free_list, *p; + for(i=size; i<=__MAX_BYTES; i += __ALIGN){ + my_free_list = free_list + FREELIST_INDEX(i); + p = *my_free_list; + if(0 != p){ + *my_free_list = p->free_list_link; + start_free = (char *)p; + end_free = start_free + i; + return chunk_alloc(size, nobjs); + } + } + end_free = 0; + start_free = (char *)malloc_alloc::allocate(bytes_to_get); + } + + heap_size += bytes_to_get; //记录此时堆空间的大小; + end_free = start_free + bytes_to_get; //指向了最后; + return chunk_alloc(size, nobjs); //上去在此调用这个函数; + } +} +``` + +**nobjs = 20; 这个是经验值,开辟空间留有余地,方便直接查找,以后就不用再次开辟空间了,提高了效率;** + +这个我们自己给不同的字节情况(小于128B的),就会知道其中发生了什么; + +SGI第二级空间配置器: + +1. **维护16个自由链表,分别有16种小型区块的配置能力;如果内存不足,调用一级空间配置器(那里有处理程序);** +2. **如果申请空间的需求大于128B,就调用一级空间配置器。** + +总结: + +1. **二级空间配置器(最后山穷水尽)--->调用一级空间配置器---->(1)、抛出异常;** +2. **调用自己编写的处理函数;** + +> **STL内存配置思想:C++STL是两级配置内存的,具体来说:第一级负责管理大块内存,要保证有类似new-handler的机制;第二级负责管理小块内存,为了更好的管理内存碎片,建立16个链表,每个链表“穿”着一块一块固定大小的内存,这16个链表(0至15)分别“穿”的内存是8、16、24…128倍数关系。需要内存时,从“合适”的链表取走(因为这里情况比较多,不能一一说道了),如果“合适”的链表内存不够用了,从内存池里拿,如果内存池不够用了,从运行时heap里拿,如果heap也溢出了,就交给第一级配置器,因为它有set_new-handler机制。所以,当堆上的东西用完之后,得赶紧还回来。** + +## 四、完整代码、测试代码、测试结果 + +### 4.1、完整代码 + +```cpp +#if 1 +#include +#include +#include +using namespace std; +//#define __THROW_BAD_ALLOC throw bad_alloc +#define __THROW_BAD_ALLOC cerr<<"Throw bad alloc, Out Of Memory."< +#define __THROW_BAD_ALLOC cerr<<"out of memory"< +class __malloc_alloc_template{ +private: + static void* oom_malloc(size_t); //对申请空间失败的处理函数 + static void* oom_realloc(void *, size_t); //对扩展空间失败处理的函数 + static void(* __malloc_alloc_oom_handler)(); //定义一个函数指针 + +public: + static void* allocate(size_t n){ //分配空间 + void *result = malloc(n); + if(0 == result) + result = oom_malloc(n); //分配空间失败,调用oom_malloc()函数 + return result; //将申请的空间的地址返回 + } + static void deallocate(void *p, size_t){ + free(p); //释放空间 + } + static void* reallocate(void *p, size_t, size_t new_sz){ + void *result = realloc(p, new_sz); //扩展新空间; + if(0 == result) //扩展失败 + oom_realloc(p,new_sz); //调用扩展空间失败的处理函数 + return result; + } +public: + //set_new_handler(Out_Of_Memory); + static void(*set_malloc_handler(void(*f)()))(){ //这是一个指针函数,函数名称:set_malloc_handler,参数:是一个函数指针,返回值:是一个函数指针; + void(*old)() = __malloc_alloc_oom_handler; //将原有空间的地址保存在old中; + __malloc_alloc_oom_handler = f; //将自己定义的函数地址给__malloc_alloc_oom_handler; + return old; //每次可以保存其上一次的地址. + } +}; + +template +void (*__malloc_alloc_template::__malloc_alloc_oom_handler)() = 0; //对定义的静态函数指针初始化为0; + +template +void* __malloc_alloc_template::oom_malloc(size_t n){ //处理空间失败的问题 + void *result; + void(* my_malloc_handler)(); //定义一个函数指针; + + for(;;){ + my_malloc_handler = __malloc_alloc_oom_handler; + if(0 == my_malloc_handler){ //自己没有定义处理空间失败的新函数 + __THROW_BAD_ALLOC; //异常抛出;程序终止; + } + (*my_malloc_handler)(); //调用自己编写的处理函数(一般都是回收空间之类的); + result = malloc(n); //在此申请分配空间 + if(result){ //申请成功 + return result; //将地址返回; + }//那么,这个程序将会一直持续到空间分配成功才最终返回. + } +} + + +template +void* __malloc_alloc_template::oom_realloc(void *p, size_t n){ + void(*my_malloc_handler)(); //函数指针 + void *result; + for(;;){ + my_malloc_handler = __malloc_alloc_oom_handler; //将这个给其赋值 + if(0 == my_malloc_handler){ //外面没有定义处理的函数 + __THROW_BAD_ALLOC; //异常抛出,程序结束. + } + (*my_malloc_handler)(); //调用自己编写的处理函数(一般都是回收空间之类的); + result = realloc(p, n); //再次扩展空间分配; + if(result){ //扩展成功,就返回 + return result; + } //一直持续成功,知道扩展空间分配成功才返回; + } +} + +typedef __malloc_alloc_template<0> malloc_alloc; //一级空间配置器:malloc_alloc; + +///////////////////////////////////////////////////////////////////////////////////// +//二级空间配置器由自由链表和内存池组成; +enum {__ALIGN = 8}; //一块链表8B +enum {__MAX_BYTES = 128}; //小于128B调用二级的 +enum {__NFREELISTS = __MAX_BYTES / __ALIGN}; //一共分配16个自由链表,负责16种次分配能力. + +template //不考虑多线程状态; +class __default_alloc_template{ +public: + static void* allocate(size_t n); //分配空间 + static void deallocate(void *p, size_t n); //销毁空间 + static void* reallocate(void *p, size_t, size_t new_sz); //扩展空间 +private: + static size_t ROUND_UP(size_t bytes){ //向上调整函数; + return (((bytes) + __ALIGN-1) & ~(__ALIGN-1)); //调为当前字节是8的整数倍. + } +private: + union obj{ //共用体 + union obj * free_list_link; //自由链表的指向 + char client_data[1]; + }; +private: + static obj* volatile free_list[__NFREELISTS]; //定义了一个指针数组; + static size_t FREELIST_INDEX(size_t bytes){ //求当前字节的自由链表的下标; + return ((bytes)+__ALIGN-1) / __ALIGN-1; + } +private: + static char *start_free; //开始空间的下标 + static char *end_free; //结束空间的下标 + static size_t heap_size; //堆空间大小 + static void *refill(size_t n); //填充函数 + static char* chunk_alloc(size_t size, int &nobjs); // +}; + + +template //以下都是对静态变量的初始化,都为0; +char* __default_alloc_template::start_free = 0; +template +char* __default_alloc_template::end_free = 0; +template +size_t __default_alloc_template::heap_size = 0; +template +typename __default_alloc_template::obj* volatile +__default_alloc_template::free_list[__NFREELISTS] = +{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +template +void* __default_alloc_template::allocate(size_t n){ //分配空间的函数 + obj * volatile *my_free_list; + obj *result; + + if(n > __MAX_BYTES){ //分配空间的大小大于128B的话,就调用一级空间配置器 + return malloc_alloc::allocate(n); + } + + my_free_list = free_list + FREELIST_INDEX(n); //free_list是二维数组名称,找往哪个链下挂; + result = *my_free_list; //取出其值,因为my_free_list是二阶指针; + + if(result == 0){ //没有内存池空间; + void *r = refill(ROUND_UP(n)); //调用refill()函数 + return r; + } + + *my_free_list = result->free_list_link; //进行挂载连接; + return result; +} + +template +void* __default_alloc_template::refill(size_t n){ //没有可用区块时,就调用refill()函数; + int nobjs = 20;//就是要分20块; + char *chunk = chunk_alloc(n, nobjs); //调用内存池函数; + obj * volatile *my_free_list; + + obj *result; + obj *current_obj, *next_obj; + int i; + + if(1 == nobjs){ //当分配到只有一块空间时,直接返回. + return chunk; + } + + my_free_list = free_list + FREELIST_INDEX(n); //找到对应的下标,进行连接的工作; + result = (obj*)chunk; + *my_free_list = next_obj = (obj*)(chunk+n); + + for(i=1; ; ++i){ + current_obj = next_obj; + next_obj = (obj*)((char*)next_obj+n); + if(nobjs - 1 == i){ //进行连接工作; + current_obj->free_list_link = 0; + break; + }else{ + current_obj->free_list_link = next_obj; + } + } + return result; +} + +template +char* __default_alloc_template::chunk_alloc(size_t size, int &nobjs){ //内存池函数 + char *result; //关键要明白以下的各种情况; + size_t total_bytes = size * nobjs; //这里的size已经是上调过的字节;求取20*size个字节的大小 + size_t bytes_left = end_free - start_free; //刚开始,遗留字节为0; + if(bytes_left >= total_bytes){ //不成立 + result = start_free; + start_free += total_bytes; + return result; + }else if(bytes_left >= size){ //不成立 + nobjs = bytes_left / size; + total_bytes = size * nobjs; + result = start_free; + start_free += total_bytes; + return result; + }else{ //走的就是下面的这条路线 + size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4); //申请2倍的total_bytes; + if(bytes_left > 0){ //遗留字节数=0,所以这条语句不成立; + obj * volatile * my_free_list = free_list + FREELIST_INDEX(bytes_left); + ((obj*)start_free)->free_list_link = *my_free_list; + *my_free_list = (obj *)start_free; + } + + start_free = (char *)malloc(bytes_to_get); //申请空间; + if(0 == start_free){ + int i; + obj * volatile *my_free_list, *p; + for(i=size; i<=__MAX_BYTES; i += __ALIGN){ + my_free_list = free_list + FREELIST_INDEX(i); + p = *my_free_list; + if(0 != p){ + *my_free_list = p->free_list_link; + start_free = (char *)p; + end_free = start_free + i; + return chunk_alloc(size, nobjs); + } + } + end_free = 0; + start_free = (char *)malloc_alloc::allocate(bytes_to_get); + } + + heap_size += bytes_to_get; //记录此时堆空间的大小; + end_free = start_free + bytes_to_get; //指向了最后; + return chunk_alloc(size, nobjs); //上去在此调用这个函数; + } +} +``` + +### 4.2、测试代码 + +```cpp +#include +#include +#include"stl_alloc.h" +using namespace std; + +int main(){ + int *p = (int *)__default_alloc_template<0,0>::allocate(sizeof(int)); + int *s = (int *)__default_alloc_template<0,0>::allocate(sizeof(int) * 4); + int *t = (int *)__default_alloc_template<0,0>::allocate(sizeof(int) * 80); + int *m = (int *)__default_alloc_template<0,0>::allocate(sizeof(double) * 10); + int *n = (int *)__default_alloc_template<0,0>::allocate(sizeof(int) * 100); + int *u = (int *)__default_alloc_template<0,0>::allocate(sizeof(int)); + int *k = (int *)__default_alloc_template<0,0>::allocate(sizeof(int) *2); + return 0; +} + + +/* +void Out_Of_Memory(){ + cout<<"Out Of Memory."<::set_malloc_handler(OMG); + //set_new_handler() + //int *p = (int*)malloc(sizeof(int)*10); + void (*pfun)() = __malloc_alloc_template<0>::set_malloc_handler(Out_Of_Memory); + int *p = (int*)__malloc_alloc_template<0>::allocate(sizeof(int) * 2073741824); + + + __malloc_alloc_template<0>::set_malloc_handler(pfun); + + if(p == NULL) + { + cout<<"Error."< + +## 五、STL中的不成文规定 + +- __打头的是内部函数 +- 一般都是前插 +- 指最后的下一个 +- fill_n() 系统函数,填充空间 + +## 六、说明 + +原创文章链接:[C++进阶系列之STL(2)---SGI版本空间配置器](https://mp.weixin.qq.com/s?__biz=MzUxMzkyNDk0Ng==&mid=2247483982&idx=1&sn=044e9861bcc16fcbe9226756d73fa094&chksm=f94c8873ce3b016588288c1cde47facd2ce6ccf91a562b11578e819b7dfbf9b9e3c8233fe6e0&scene=21#wechat_redirect) diff --git "a/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_STL\345\255\246\344\271\240\346\200\235\346\203\263.md" "b/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_STL\345\255\246\344\271\240\346\200\235\346\203\263.md" new file mode 100644 index 0000000..7939f94 --- /dev/null +++ "b/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_STL\345\255\246\344\271\240\346\200\235\346\203\263.md" @@ -0,0 +1,21 @@ +STL学习思想: + +1、**模版:一定要注意参数和返回值的模版;** + +2、**STL一系列的API:一定要注意返回值;** + +3、**容器中的都是值拷贝,而不是引用,在执行插入时,内部实行拷贝动作,所以STL中插入类时,一般都必须:无参构造函数,拷贝构造函数,重载=运算符,必须的自己重写,达到深拷贝!!!** + +4、**一元谓词:函数只有一个参数,谓词:代表函数的返回值必须为bool类型;二元谓词:函数只有二个参数,谓词:代表函数的返回值必须为bool类型;** + +5、**算法和具体的数据类型相分离:通过函数对象(仿函数)来实现,本质:函数指针!!!** + +6、**容器和具体的数据类型相分离,通过模板技术实现的;** + +7、**一个容器都有其对应的迭代器进行访问;** + +8、**所有的容器都是通过空间配置器来分配空间的;** + +9、**函数适配器有一元的、二元的,就是函数参数的绑定;** + +原文链接:[C++进阶系列之STL(7)---STL学习思想](https://mp.weixin.qq.com/s?__biz=MzUxMzkyNDk0Ng==&mid=2247484011&idx=1&sn=f08da663d2707c7bcf387804bb0b8918&chksm=f94c8856ce3b01403abf9fbcadeba83d81f20018f864125ad6ddbb7b60a55e29185a3316c360&scene=21#wechat_redirect) diff --git "a/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_STL\346\237\245\346\211\276_\346\216\222\345\272\217_\346\233\277\346\215\242_\351\233\206\345\220\210\347\256\227\346\263\225.md" "b/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_STL\346\237\245\346\211\276_\346\216\222\345\272\217_\346\233\277\346\215\242_\351\233\206\345\220\210\347\256\227\346\263\225.md" new file mode 100644 index 0000000..3a83097 --- /dev/null +++ "b/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_STL\346\237\245\346\211\276_\346\216\222\345\272\217_\346\233\277\346\215\242_\351\233\206\345\220\210\347\256\227\346\263\225.md" @@ -0,0 +1,428 @@ +- [一、STL查找算法](#一stl查找算法) +- [二、STL排序算法](#二stl排序算法) +- [三、STL拷贝替换](#三stl拷贝替换) +- [四、STL集合算法](#四stl集合算法) +- [五、说明](#五说明) + +## 一、STL查找算法 + +代码如下: + +```cpp +#include +#include +#include +using namespace std; + + +//查找算法!!! +void main_adjacent_find(){ + vector v1; + + v1.push_back(1); + v1.push_back(2); + v1.push_back(2); + v1.push_back(3); + v1.push_back(5); + + //查找第一个元素重复的位置; + vector::iterator it; + it = adjacent_find(v1.begin(), v1.end()); //查找第一个重复出现的数字; + if(it == v1.end()){ + cout<<"没有找到重复的元素"< v1; + + //0 1 2 3 4 5.......n-1 + //二分法,10次就可以找到了; + v1.push_back(1); + v1.push_back(3); + v1.push_back(5); + v1.push_back(7); + v1.push_back(9); + + bool b = binary_search(v1.begin(), v1.end(), 7); + if(b == true){ + cout<<"找到了"< v1; + + v1.push_back(1); + v1.push_back(3); + v1.push_back(5); + v1.push_back(7); + v1.push_back(7); + v1.push_back(9); + v1.push_back(7); + + int num = count(v1.begin(), v1.end(), 7); + + cout<<"num:"< 3){ + return true; + } + return false; +} + +void main_count_if(){ + vector v1; + + v1.push_back(1); + v1.push_back(3); + v1.push_back(5); + v1.push_back(7); + v1.push_back(9); + + int num = count_if(v1.begin(), v1.end(), GreatThree); + cout<<"num:"< v1; + + v1.push_back(1); + v1.push_back(3); + v1.push_back(5); + v1.push_back(7); + v1.push_back(9); + + vector::iterator it; + it = find(v1.begin(), v1.end(), 5); + cout<<*it< v1; + + v1.push_back(1); + v1.push_back(3); + v1.push_back(5); + v1.push_back(7); + v1.push_back(9); + + vector::iterator it; + it = find_if(v1.begin(), v1.end(), GreatThree); //返回的是第一个回调函数所指向的迭代器; + cout<<*it< + +## 二、STL排序算法 + +代码如下: + +```cpp +#include +#include +#include +#include +#include +using namespace std; + +//排序算法 +void printV(vector &v){ + vector::iterator it; + + for(it = v.begin(); it != v.end(); it++){ + cout<<*it<<" "; + } + cout< v1; + v1.push_back(1); + v1.push_back(3); + v1.push_back(5); + + vector v2; + v2.push_back(2); + v2.push_back(4); + v2.push_back(6); + + vector v3; + v3.resize(v1.size() + v2.size()); + merge(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin()); + + printV(v3); +} + +class Student{ + public: + Student(string name, int id){ + m_name = name; + m_id = id; + } + public: + string m_name; + int m_id; +}; + +bool Compare(Student s1, Student s2){ + return (s1.m_id < s2.m_id); +} + +void main_sort(){ + Student s1("老大", 1); + Student s2("老二", 2); + Student s3("老三", 3); + Student s4("老四", 4); + + vector v1; + v1.push_back(s4); + v1.push_back(s1); + v1.push_back(s3); + v1.push_back(s2); + + vector::iterator it; + for(it = v1.begin(); it != v1.end(); it++){ + cout<m_id<<"\t"<m_name<m_id<<"\t"<m_name< v1; + v1.push_back(1); + v1.push_back(3); + v1.push_back(5); + v1.push_back(7); + + //对动态数组进行随机打乱 + random_shuffle(v1.begin(), v1.end()); + + printV(v1); + + //对字符串随机的打乱顺序; + string str = "abcdefg"; + random_shuffle(str.begin(), str.end());; + cout<<"str:"< v1; + v1.push_back(1); + v1.push_back(3); + v1.push_back(5); + v1.push_back(7); + + reverse(v1.begin(), v1.end()); + printV(v1); +} + +int main(void){ + main_merge(); //归并排序 + main_sort(); + main_random_shuffle(); + main_reverse(); + + return 0; +} +``` + +运行结果: + +
+ +## 三、STL拷贝替换 + +代码如下: + +```cpp +#include +#include //accumulate()函数的头文件; +#include +#include +#include +using namespace std; + +//拷贝替换 + +void printV(vector &v){ + vector::iterator it; + + for(it = v.begin(); it != v.end(); it++){ + cout<<*it<<" "; + } + cout< v1; + v1.push_back(1); + v1.push_back(3); + v1.push_back(5); + + vector v2; + v2.resize(v1.size()); //调整容器大小的函数; + + copy(v1.begin(), v1.end(), v2.begin()); //拷贝容器函数; + + printV(v2); + +} + +void main_replace(){ + vector v1; + v1.push_back(1); + v1.push_back(3); + v1.push_back(3); + v1.push_back(3); + v1.push_back(5); + + replace(v1.begin(), v1.end(), 3, 8); //替换函数,将前面的数据都用后面的数据替换; + printV(v1); +} + +bool great_equal(int &n){ + if(n >= 5){ + return true; + }else{ + return false; + } +} +void main_replace_if(){ + vector v1; + v1.push_back(1); + v1.push_back(8); + v1.push_back(8); + v1.push_back(8); + v1.push_back(5); + + replace_if(v1.begin(), v1.end(), great_equal, 1); //这里就是可以替换范围内的数据,通过谓词进行控> +制!!,将great_equal范围内的数字(返回值为真的)进行替换!!! + printV(v1); +} + +void main_swap(){ + vector v1; + v1.push_back(1); + v1.push_back(3); + v1.push_back(5); + + vector v2; + v2.push_back(2); + v2.push_back(4); + v2.push_back(6); + + swap(v1, v2); //交换了v1、v2容器中的内容!!! + printV(v1); +} + +void main_accumulate(){ + vector v1; + v1.push_back(1); + v1.push_back(3); + v1.push_back(5); + + int tmp = accumulate(v1.begin(), v1.end(), 100); //求和函数,加上一个初始值100; + cout< v1; + v1.push_back(1); + v1.push_back(3); + v1.push_back(5); + + fill(v1.begin(), v1.end(), 8); //将容器中的数值全都改为8; + printV(v1); +} +int main(void){ + main_copy(); //拷贝 + main_replace(); //替换 基础数据类型 + main_replace_if(); //替换范围 自定义数据类型 + main_swap(); //交换容器中的内容 + main_accumulate(); //求和函数 + main_fill(); + + + return 0; +} +``` + +运行结果: + +
+ +## 四、STL集合算法 + +代码如下: + +```cpp +#include +#include +#include +#include +using namespace std; + +void printV(vector &v){ + vector::iterator it; + + for(it = v.begin(); it != v.end(); it++){ + cout<<*it<<" "; + } + cout< v1; + v1.push_back(1); + v1.push_back(3); + v1.push_back(5); + + vector v2; + v2.push_back(1); + v2.push_back(1); + v2.push_back(1); + vector v3; + v3.resize(v1.size() + v2.size()); + set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin()); //集合中取并集;没有填满的空间默,认为0; + printV(v3); + + return 0; +} +``` + +运行结果: + +
+ +## 五、说明 + +原创文章链接:[C++进阶系列之STL(10)---STL查找、排序、替换、集合算法](https://mp.weixin.qq.com/s?__biz=MzUxMzkyNDk0Ng==&mid=2247484014&idx=1&sn=13eae32f965cd1cdbf6d590b9e05aa43&chksm=f94c8853ce3b0145188f0b4d516b83b2ff5a887fc840b123de29a6cd5bef70633eb6bced1c56&scene=21#wechat_redirect) diff --git "a/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_STL\347\232\204\346\241\206\346\236\266\345\256\236\347\216\260.md" "b/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_STL\347\232\204\346\241\206\346\236\266\345\256\236\347\216\260.md" new file mode 100644 index 0000000..9c3f48b --- /dev/null +++ "b/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_STL\347\232\204\346\241\206\346\236\266\345\256\236\347\216\260.md" @@ -0,0 +1,471 @@ +- [一、模板的特化](#一模板的特化) +- [二、萃取](#二萃取) +- [三、整个搭建框架](#三整个搭建框架) + - [3.1、框架代码](#31框架代码) + - [3.2、测试代码](#32测试代码) + - [3.3、测试结果](#33测试结果) +- [四、分析](#四分析) +- [五、说明](#五说明) + +## 一、模板的特化 + +泛化模板:对各种不同版本的特殊化处理---->特化; + +## 二、萃取 + +**拿相同的代码提取不同的类型;** + +## 三、整个搭建框架 + +
+ +**在这里已经不关心空间配置器是如何实现的;** + +### 3.1、框架代码 + +```cpp +#ifndef _MEMORY_H_ +#define _MEMORY_H_ + +#include"stl_alloc.h" +#include"stl_iterator.h" +#include"type_traits.h" +#include"stl_construct.h" +#include"stl_uninitialized.h" + +#endif +//////////////////////////////////////////////////////////////////////////////////// + +#if 1 +#include +#include +#include +using namespace std; +//#define __THROW_BAD_ALLOC throw bad_alloc +#define __THROW_BAD_ALLOC cerr<<"Throw bad alloc, Out Of Memory."< +#define __THROW_BAD_ALLOC cerr<<"out of memory"< +class __malloc_alloc_template{ +private: + static void* oom_malloc(size_t); + static void* oom_realloc(void *, size_t); + static void(* __malloc_alloc_oom_handler)(); + +public: + static void* allocate(size_t n){ + void *result = malloc(n); + if(0 == result){ + result = oom_malloc(n); + } + return result; + } + static void deallocate(void *p, size_t){ + free(p); + } + static void* reallocate(void *p, size_t, size_t new_sz){ + void *result = realloc(p, new_sz); + if(0 == result){ + oom_realloc(p,new_sz); + } + return result; + } +public: + //set_new_handler(Out_Of_Memory); + static void(*set_malloc_handler(void(*f)()))(){ + void(*old)() = __malloc_alloc_oom_handler; + __malloc_alloc_oom_handler = f; + return old; + } +}; + +template +void (*__malloc_alloc_template::__malloc_alloc_oom_handler)() = 0; + +template +void* __malloc_alloc_template::oom_malloc(size_t n){ + void *result; + void(* my_malloc_handler)(); + + for(;;){ + my_malloc_handler = __malloc_alloc_oom_handler; + if(0 == my_malloc_handler){ + __THROW_BAD_ALLOC; + } + (*my_malloc_handler)(); + result = malloc(n); + if(result){ + return result; + } + } +} + + +template +void* __malloc_alloc_template::oom_realloc(void *p, size_t n){ + void(*my_malloc_handler)(); + void *result; + for(;;){ + my_malloc_handler = __malloc_alloc_oom_handler; + if(0 == my_malloc_handler){ + __THROW_BAD_ALLOC; + } + (*my_malloc_handler)(); + result = realloc(p, n); + if(result){ + return result; + } + } +} + +typedef __malloc_alloc_template<0> malloc_alloc; + +///////////////////////////////////////////////////////////////////////////////////// + +enum {__ALIGN = 8}; +enum {__MAX_BYTES = 128}; +enum {__NFREELISTS = __MAX_BYTES / __ALIGN}; + +template +class __default_alloc_template{ +public: + static void* allocate(size_t n); + static void deallocate(void *p, size_t n); + static void* reallocate(void *p, size_t, size_t new_sz); +private: + static size_t ROUND_UP(size_t bytes){ + return (((bytes) + __ALIGN-1) & ~(__ALIGN-1)); + } +private: + union obj{ + union obj * free_list_link; + char client_data[1]; + }; +private: + static obj* volatile free_list[__NFREELISTS]; + static size_t FREELIST_INDEX(size_t bytes){ + return ((bytes)+__ALIGN-1) / __ALIGN-1; + } +private: + static char *start_free; + static char *end_free; + static size_t heap_size; + static void *refill(size_t n); + static char* chunk_alloc(size_t size, int &nobjs); +}; + + +template +char* __default_alloc_template::start_free = 0; +template +char* __default_alloc_template::end_free = 0; +template +size_t __default_alloc_template::heap_size = 0; +template +typename __default_alloc_template::obj* volatile +__default_alloc_template::free_list[__NFREELISTS] = +{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +template +void* __default_alloc_template::allocate(size_t n){ + obj * volatile *my_free_list; + obj *result; + + if(n > __MAX_BYTES){ + return malloc_alloc::allocate(n); + } + + my_free_list = free_list + FREELIST_INDEX(n); + result = *my_free_list; + + if(result == 0){ + void *r = refill(ROUND_UP(n)); + return r; + } + + *my_free_list = result->free_list_link; + return result; +} + +template +void* __default_alloc_template::refill(size_t n){ + int nobjs = 20; + char *chunk = chunk_alloc(n, nobjs); + obj * volatile *my_free_list; + + obj *result; + obj *current_obj, *next_obj; + int i; + + if(1 == nobjs){ + return chunk; + } + + my_free_list = free_list + FREELIST_INDEX(n); + result = (obj*)chunk; + *my_free_list = next_obj = (obj*)(chunk+n); + + for(i=1; ; ++i){ + current_obj = next_obj; + next_obj = (obj*)((char*)next_obj+n); + if(nobjs - 1 == i){ + current_obj->free_list_link = 0; + break; + }else{ + current_obj->free_list_link = next_obj; + } + } + return result; +} + +template +char* __default_alloc_template::chunk_alloc(size_t size, int &nobjs){ + char *result; + size_t total_bytes = size * nobjs; + size_t bytes_left = end_free - start_free; + if(bytes_left >= total_bytes){ + result = start_free; + start_free += total_bytes; + return result; + } + else if(bytes_left >= size){ + nobjs = bytes_left / size; + total_bytes = size * nobjs; + result = start_free; + start_free += total_bytes; + return result; + }else{ + size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4); + if(bytes_left > 0){ + obj * volatile * my_free_list = free_list + FREELIST_INDEX(bytes_left); + ((obj*)start_free)->free_list_link = *my_free_list; + *my_free_list = (obj *)start_free; + } + + start_free = (char *)malloc(bytes_to_get); + if(0 == start_free){ + int i; + obj * volatile *my_free_list, *p; + for(i=size; i<=__MAX_BYTES; i += __ALIGN){ + my_free_list = free_list + FREELIST_INDEX(i); + p = *my_free_list; + if(0 != p){ + *my_free_list = p->free_list_link; + start_free = (char *)p; + end_free = start_free + i; + return chunk_alloc(size, nobjs); + } + } + end_free = 0; + start_free = (char *)malloc_alloc::allocate(bytes_to_get); + } + + heap_size += bytes_to_get; + end_free = start_free + bytes_to_get; + return chunk_alloc(size, nobjs); + } +} + +///////////////////////////////////////////////////////////////////////////////// +#ifdef __USE_MALLOC +typedef malloc_alloc alloc; +#else +typedef __default_alloc_template<0,0> alloc; +#endif + +template +class simple_alloc{ +public: + static T* allocate(size_t n){ + return 0==n ? 0 : (T*)Alloc::allocate(n * sizeof(T)); + } + static T* allocate(void){ + return (T*)Alloc::allocate(sizeof(T)); + } + static void deallocate(T *p, size_t n){ + if(0!=n) Alloc::deallocate(p, n*sizeof(T)); + } + static void deallocate(T *p){ + Alloc::deallocate(p,sizeof(T)); + } +}; +////////////////////////////////////////////////////////////////////////////////////////// +#ifndef __STL_CONFIG_H +#define __STL_CONFIG_H + +//#define __USE_MALLOC + + +#endif + +////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef __STL_CONSTRUCT_H +#define __STL_CONSTRUCT_H + +template +void construct(T1* p, const T2& value){ + new (p) T1(value); +} + +#endif +//////////////////////////////////////////////////////////////////////////////////////// +#ifndef __STL_ITERATOR_H +#define __STL_ITERATOR_H + +template +T* value_type(const T*){ + return (T*)(0); +} + +#endif +//////////////////////////////////////////////////////////////////////////////////////// +#ifndef __STL_UNINITIALIZED_H +#define __STL_UNINITIALIZED_H + + +template +ForwardIterator _uninitialized_fill_n_aux(ForwardIterator first, Size n, + const T &x, _true_type){ + cout<<"yyyyyyyyyyy"< +ForwardIterator _uninitialized_fill_n_aux(ForwardIterator first, Size n, + const T &x, _false_type){ + ForwardIterator cur = first; + for(; n>0; --n, ++cur){ + cout<<"xxxxxxxxxxxx"< +ForwardIterator _uninitialized_fill_n(ForwardIterator first, Size n, const T &x, T1*){ + //typedef _false_type is_POD; + typedef typename _type_traits::is_POD_type is_POD; + //return _uninitialized_fill_n(first, n, x, _false_type()); + return _uninitialized_fill_n_aux(first, n, x, is_POD()); +} + +template +ForwardIterator uninitialized_fill_n(ForwardIterator first, Size n, const T &x){ + return _uninitialized_fill_n(first, n, x, value_type(first)); +} + +#endif +//////////////////////////////////////////////////////////////////////////////////////// +#ifndef __STL_VECTOR_H +#define __STL_VECTOR_H + +template +class vector{ +public: + typedef T value_type; + typedef value_type* pointer; + typedef value_type* iterator; + typedef value_type& reference; + typedef size_t size_type; + typedef ptrdiff_t difference_type; +public: + vector() : start(0), finish(0), end_of_storage(0){} + vector(size_type n){ + fill_initialize(n, T()); + } +protected: + void fill_initialize(size_type n, const T &value){ + start = allocate_and_fill(n, value); + finish = start + n; + end_of_storage = finish; + } + iterator allocate_and_fill(size_type n, const T &x){ + iterator result = data_allocator::allocate(n); + uninitialized_fill_n(result, n, x); + return result; + } +protected: + typedef simple_alloc data_allocator; + iterator start; + iterator finish; + iterator end_of_storage; +}; + + +#endif +//////////////////////////////////////////////////////////////////////////////////////// +#ifndef __TYPE_TRAITS_H +#define __TYPE_TRAITS_H + + +// + +struct _true_type {}; +struct _false_type{}; + +template +struct _type_traits { + typedef _true_type this_dummy_member_must_be_first; + typedef _false_type has_trivial_default_constructor; + typedef _false_type has_trivial_copy_constructor; + typedef _false_type has_trivial_assignment_operator; + typedef _false_type has_trivial_destructor; + typedef _false_type is_POD_type; +}; + +template<> +struct _type_traits { + typedef _true_type has_trivial_default_constructor; + typedef _true_type has_trivial_copy_constructor; + typedef _true_type has_trivial_assignment_operator; + typedef _true_type has_trivial_destructor; + typedef _true_type is_POD_type; +}; + +#endif +/////////////////////////////////////////////////////////////////////////////////////// +#ifndef _VECTOR_H_ +#define _VECTOR_H_ + +#include"memory.h" +#include"stl_vector.h" + +#endif +/////////////////////////////////////////////////////////////////////////////////////// +``` + +### 3.2、测试代码 + +```cpp +#include +#include +#include"vector.h" +using namespace std; + +class Test{}; + +int main(){ + vector v(10); //开辟了10个元素的空间; + return 0; +``` + +### 3.3、测试结果 + +
+ +## 四、分析 + +1. **vector的空间灵活性更高;** +2. **POD:也就是标量型别,也就是传统的型别,采取最保险安全的做法,调用构造函数;否则的话,就是调用系统的,基本类型就是true;** +3. **空构造了2个类型,针对不同萃取得到其_false_type或_true_type ; 就可以调用不同的函数,进行空间的分配,存在效率上的差异,_true_type:将调用系统的填充函数,效率比较高;** +4. **容器、算法单独好实现,关键是通用性,模板是一种很好的解决方案,真正关键之处还在 : 迭代器的实现;** +5. **空间配置器负责分配、回收空间,只有一个;迭代器针对不同的容器有不同的实现方法!!!** + +## 五、说明 + +原创文章链接:[C++进阶系列之STL(3)---STL的框架实现](https://mp.weixin.qq.com/s?__biz=MzUxMzkyNDk0Ng==&mid=2247484007&idx=1&sn=043602c820fc2d48716ac93ff36ea793&chksm=f94c885ace3b014c879f5b0bf9b7a2b7881474c4698c9ee0c76e1ff193254380ab60094211a3&scene=21#wechat_redirect) diff --git "a/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_find_if()_plus_for_each()\347\232\204\347\224\250\346\263\225.md" "b/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_find_if()_plus_for_each()\347\232\204\347\224\250\346\263\225.md" new file mode 100644 index 0000000..098e922 --- /dev/null +++ "b/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_find_if()_plus_for_each()\347\232\204\347\224\250\346\263\225.md" @@ -0,0 +1,230 @@ +- [一、STL算法->find_if()](#一stl算法-find_if) +- [二、STL算法->plus的使用](#二stl算法-plus的使用) +- [三、STL算法->for_each()](#三stl算法-for_each) +- [四、for_each()和transform()的区别](#四for_each和transform的区别) +- [五、说明](#五说明) + +## 一、STL算法->find_if() + +代码如下: + +```cpp +#include +#include +#include +#include +using namespace std; + +template +class IsDiv{ + public: + IsDiv(const Type &divisor){ + this->divisor = divisor; + } + + bool operator()(Type &t){ + return t%divisor == 0; + } + protected: + private: + Type divisor; +}; + +int main(void){ + vector v2; + + for(int i = 10; i < 33; i++){ + v2.push_back(i); + } + int a = 4; + IsDiv myDiv(a); + + //find_if(v2.begin(), v2.end(), myDiv); + vector::iterator it; + it =find_if(v2.begin(), v2.end(), IsDiv(a) ); + if(it == v2.end()){ + cout<<"容器中没有值是4的元素"< + +## 二、STL算法->plus的使用 + +代码如下: + +```cpp +#include +#include +#include +#include +using namespace std; + +//plus 预定义好的函数对象,能实现不同数据 + 算法; +//实现了数据类型和算法的分离======》通过函数对象技术实现的; +// +//思考,怎么知道plus是2个参数------>多看看源码; +void main21(){ + plus intAdd; + int x = 10; + int y = 20; + + int z = intAdd(x, y); + cout<<"z:"< stringAdd; + + string s1 = "aaa"; + string s2 = "bbb"; + string s3 = stringAdd(s1, s2); + cout<<"s3:"< v1; + v1.push_back("bbb"); + v1.push_back("aaa"); + v1.push_back("ccc"); + v1.push_back("zzz"); + v1.push_back("ccc"); + v1.push_back("ccc"); + + sort(v1.begin(), v1.end(), greater()); //降序排列; + vector::iterator it; + for(it = v1.begin(); it != v1.end(); it++){ + cout<<*it<有2个参数,left参数来自容器,right参数来自sc, + //bind2nd就是函数适配器:把预定义函数对象和第二个参数进行绑定;` + int num = count_if(v1.begin(), v1.end(), bind2nd(equal_to(), sc)); + cout<<"num:"< + +## 三、STL算法->for_each() + +代码如下 + +```cpp +#include +#include +#include +#include +using namespace std; + +void printV(vector &v){ + vector::iterator it; + + for(it = v.begin(); it != v.end(); it++){ + cout<<*it<<" "; + } + cout< v1; + v1.push_back(1); + v1.push_back(3); + v1.push_back(5); + + printV(v1); + + //第三个参数是:函数对象/回掉函数 + //for_each(v1.begin(), v1.end(), showElem);//利用的是回调函数 + for_each(v1.begin(), v1.end(), MyShow()); //利用的是函数对象(这个类中重载了()) + //函数的返回值是函数对象 + cout< + +## 四、for_each()和transform()的区别 + +代码如下: + +```cpp +#include +#include +#include +#include +using namespace std; + +void showElem(int &n){ + cout< v1; + v1.push_back(1); + v1.push_back(3); + v1.push_back(5); + + vector v2 = v1; + + for_each(v1.begin(), v1.end(), showElem); + transform(v2.begin(), v2.end(), v2.begin(), showElem2);//transform对回调函数的要求;返回值必须有 + cout< + +## 五、说明 + +原创文章链接:[C++进阶系列之STL(9)---find_if(),plus,for_each()的用法](https://mp.weixin.qq.com/s?__biz=MzUxMzkyNDk0Ng==&mid=2247484013&idx=1&sn=33af2e60e7c8648ff419a87dde7cd24a&chksm=f94c8850ce3b0146b76800c82e23ce29da8f0286cb919a66da29524c0c04b4109cf1d52093ef&scene=21#wechat_redirect) diff --git "a/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_set_map\347\233\270\345\205\263\346\223\215\344\275\234.md" "b/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_set_map\347\233\270\345\205\263\346\223\215\344\275\234.md" new file mode 100644 index 0000000..2090ab1 --- /dev/null +++ "b/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_set_map\347\233\270\345\205\263\346\223\215\344\275\234.md" @@ -0,0 +1,500 @@ +- [一、set的基本操作](#一set的基本操作) + - [1.1、set的删除、插入操作](#11set的删除插入操作) + - [1.2、set的排序](#12set的排序) + - [1.3、set中为复杂数据类型时的排序](#13set中为复杂数据类型时的排序) + - [1.4、set中迭代器的使用](#14set中迭代器的使用) +- [二、multiset的基本操作](#二multiset的基本操作) +- [三、map的基本操作](#三map的基本操作) + - [3.1、map元素的添加、遍历、删除](#31map元素的添加遍历删除) + - [3.2、map中4种初始化的分析](#32map中4种初始化的分析) + - [3.3、map查找](#33map查找) +- [四、multimap的基本操作](#四multimap的基本操作) +- [五、说明](#五说明) + +## 一、set的基本操作 + +### 1.1、set的删除、插入操作 + +代码如下: + +```cpp +#include +#include +using namespace std; + +//set底层是红黑树,其所包含的元素是唯一的,集合中的元素按一定的顺序排列,元素插入过程是按排序规则插入,所> +以不能指定插入位置; +int main(void){ + //set 集合 元素唯一 自动排序(默认情况下是从小到大) 不能按照[]方式插入元素 底层红黑树 + + set set1; + + for(int i = 0; i < 5; i++){ + set1.insert(34); + set1.insert(24); + set1.insert(14); + set1.insert(84); + set1.insert(-4); + } + //插入重复的元素 + set1.insert(100); + set1.insert(100); + set1.insert(100); + set1.insert(100); + + set::iterator it; + for(it = set1.begin(); it != set1.end(); it++){ + cout<<*it<<" "; + } + cout<::iterator it = set1.begin(); + cout<<*it<<" "; + set1.erase(set1.begin()); + } + cout< +#include +using namespace std; + +//对于基础数据可以进行排序,复杂数据类型的排序是怎么回事?------>仿函数解决 +int main(void){ + set set1; //默认排序从小到大 + set > set2; //集合是从小到大 + set > set3; //集合从大到小的输出; + + for(int i = 0; i < 5; i++){ + set3.insert(11); + set3.insert(45); + set3.insert(99); + set3.insert(77); + set3.insert(66); + } + + //从大到小的排序 + set >::iterator it; + for(it = set3.begin(); it != set3.end(); it++){ + cout<<*it<<" "; + } + cout< +#include +#include +using namespace std; + +class Student{ + public: + Student(const char *name, int age){ + strcpy(this->name, name); + this->age = age; + } + public: + char name[32]; + int age; + +}; + +//仿函数:重载了(), +struct FunStudent{ + bool operator()(const Student &left, const Student &right){ + if(left.age < right.age){ //左边的小,就返回为真!!从小到大进行排序 + return true; + }else{ + return false; + } + } +}; +int main(void){ +/* + Student s1("s1", 31); + Student s2("s2", 22); + Student s3("s3", 55); + Student s4("s4", 11); + Student s5("s5", 31); //如果2个31岁,能插入成功吗? + //如何知道插入的结果,看函数的返回值 + + set set1; //集合中插入的是学生类型(复杂数据类型),会调用这个仿函数 + set1.insert(s1); + set1.insert(s2); + set1.insert(s3); + set1.insert(s4); + set1.insert(s5); + + set::iterator it; + for(it = set1.begin(); it != set1.end(); it++){ + cout<age<<" "<name< set1; //集合中插入的是学生类型(复杂数据类型),会调用这个仿函数 + pair::iterator, bool> pair1 = set1.insert(s1); + if(pair1.second == true){ //insert()的返回值是pair(对组)类型; + cout<<"插入s1成功"<::iterator, bool> pair5 = set1.insert(s5); + if(pair5.second == true){ + cout<<"插入s1成功"<::iterator it; + for(it = set1.begin(); it != set1.end(); it++){ + cout<age<<" "<name< +#include +#include +using namespace std; + + +//返回值为pair的类型要学会使用; +int main(void){ + set set1; + + for(int i = 0; i < 10; i++){ + set1.insert(i+1); + } + + set::iterator it; + for(it = set1.begin(); it != set1.end(); it++){ + cout<<*it<<" "; + } + cout<::iterator it0 = set1.find(5); + cout<<"it0:"<<*it0<::iterator it1 = set1.lower_bound(5); //大于等于5的元素的迭代器的位置 + cout<<"it1:"<<*it1<::iterator, set::iterator> mypair = set1.equal_range(5); //函数的返回值为对组 + cout<<*mypair.first< +#include +using namespace std; + +int main(void){ + multiset set1; + int tmp = 0; + + cout<<"请输入multiset集合中的值:"; + cin>>tmp; + + while(tmp != 0){ + set1.insert(tmp); + cout<<"请输入multiset集合中的值:"; + cin>>tmp; + } + + multiset::iterator it; + for(it = set1.begin(); it != set1.end(); it++){ + cout<<*it<<" "; + } + cout<::iterator it = set1.begin(); + cout<<*it<<" "; + set1.erase(it); + } + cout< +#include +#include +using namespace std; + +//map元素的添加、遍历、删除 +int main(void){ + map map1; + //因为map是key-value结构,所以可以做成pair(对组) + //初始化map: + //方法1、 + map1.insert(pair(1, "teacher01")); + map1.insert(pair(2, "teacher02")); + //方法2、 + map1.insert(make_pair(3, "teacher03")); + map1.insert(make_pair(4, "teacher04")); + //方法3、 + map1.insert(map::value_type(5, "teacher05")); + map1.insert(map::value_type(6, "teacher06")); + //方法4、 + map1[7] = "teacher07"; + map1[8] = "teacher08"; + + + //容器的遍历 + map::iterator it; + for(it = map1.begin(); it != map1.end(); it++){ + cout<first<<" "<second<::iterator it = map1.begin(); + cout<first<<" "<second< +#include +using namespace std; + +//前三种初始化方法的返回值都为:pair(iterator, bool) +//插入四中方法的异同 +//结论:前三种方法,若key已经存在,此时在进行插入,将会报错; +//方法4、若key已经存在,则修改; +int main(void){ + map map1; + //方法1、 + pair::iterator, bool> mypair1 = map1.insert(pair(1, "teacher01")); + map1.insert(pair(2, "teacher02")); + //方法2、 + pair::iterator, bool> mypair3 = map1.insert(make_pair(3, "teacher03")); + map1.insert(make_pair(4, "teacher04")); + //方法3、 + pair::iterator, bool> mypair5 = map1.insert(map::value_type(5, "tea +cher05")); + if(mypair5.second != true){ + cout<<"key 5插入失败"<first<<" "<second<::iterator, bool> mypair6 = map1.insert(map::value_type(5, "tea +cher06")); + if(mypair6.second != true){ + cout<<"key 5插入失败"<first<<" "<second<::iterator it; + for(it = map1.begin(); it != map1.end(); it++){ + cout<first<<"\t"<second< +#include +using namespace std; + +int main(void){ + map map1; + //因为map是key-value结构,所以可以做成pair(对组) + //初始化map: + //方法1、 + map1.insert(pair(1, "teacher01")); + map1.insert(pair(2, "teacher02")); + //方法2、 + map1.insert(make_pair(3, "teacher03")); + map1.insert(make_pair(4, "teacher04")); + //方法3、 + map1.insert(map::value_type(5, "teacher05")); + map1.insert(map::value_type(6, "teacher06")); + //方法4、 + map1[7] = "teacher07"; + map1[8] = "teacher08"; + + + //容器的遍历 + map::iterator it; + for(it = map1.begin(); it != map1.end(); it++){ + cout<first<<" "<second<::iterator it2 = map1.find(2); + if(it2 == map1.end()){ + cout<<"key 100的值不存在"<first<<"\t"<second<::iterator, map::iterator> mypair = map1.equal_range(5); //此时返回2个迭代器,形成一个pair(对组) + //第一个迭代器,是>=5的位置 + //第二个迭代器是 >5的位置 + //使用第一个迭代器 + if(mypair.first == map1.end()){ + cout<<"第一个迭代器,是>=5的位置不存在"<first<<"\t"<second<5的位置不存在"<first<<"\t"<second< +#include +#include +using namespace std; + +//multimap的重要应用场景:数据分组; +class Person{ + public: + string name; + int age; + string tel; + double saly; +}; + +int main(void){ + Person p1, p2, p3, p4, p5; + p1.name = "王1"; + p1.age = 31; + p2.name = "王2"; + p2.age = 32; + p3.name = "张3"; + p3.age = 33; + p4.name = "张4"; + p4.age = 34; + p5.name = "赵5"; + p5.age = 35; + multimap map1; + //sale部门 + map1.insert(make_pair("sale", p1)); + map1.insert(make_pair("sale", p2)); + //development + map1.insert(make_pair("development", p3)); + map1.insert(make_pair("development", p4)); + //financial + map1.insert(make_pair("Financial", p5)); + + multimap::iterator it; + for(it = map1.begin(); it != map1.end(); it++){ + //将age=32的人,名字更改为name32; + if(it->second.age == 32){ + it->second.name = "name32"; + } + cout<first<<"\t"<second.name<::iterator it1 = map1.find("development"); + int tag = 0; + while(it1 != map1.end() && tag < num){ + cout<first<<"\t"<second.name< +#include +using namespace std; + +//数组元素的添加和删除、获取 +int main(void){ + vector v1; + + v1.push_back(1); + v1.push_back(2); + v1.push_back(3); + + cout<<"len:"< 0){ + cout< +#include +using namespace std; + +void printV(vector &v){ + for(int i = 0; i < v.size(); i++){ + cout< v1; + + v1.push_back(1); + v1.push_back(3); + v1.push_back(5); + v1.push_back(7); + + vector v2 = v1; //对象初始化 + vector v3(v1.begin(), v1.begin()+2); //对象初始化 +/* + //vector的遍历 + for(i =a 0; i < v1.size(); i++){ + v1[i] = i + 1; + } + int i; + for(i = 0; i < v1.size(); i++){ + cout< v5(10); //前10个元素初始化为0; + v5.push_back(100); + v5.push_back(200); + printV(v5); + return 0; +} +``` + +### 1.3、vector中迭代器的正向和反向遍历 + +代码如下: + +```cpp +#include +#include +using namespace std; + +int main(void){ + vector v1(10); //初始化空间的值都为0; + for(int i = 0; i < 10; i++){ + v1[i] = i+1; + } +//迭代器 +//1(begin)、3、5 、(end) 当it = v1.end()的时候,说明这个容器已经遍历完了; +//end()的位置,应该是5的后面; + //正向遍历 + vector::iterator it; + for(it = v1.begin(); it != v1.end(); it++){ + cout<<*it<<" "; + } + cout<::reverse_iterator rit; + for(rit = v1.rbegin(); rit != v1.rend(); rit++){ + cout<<*rit<<" "; + } + cout< +#include +using namespace std; + +void printV(vector &v){ + for(int i = 0; i < v.size(); i++){ + cout< v1(10); + + for(int i = 0; i < 10; i++){ + v1[i] = i + 1; + } + //区间删除 + v1.erase(v1.begin(), v1.begin()+3); //删除开始的3个元素 + //指定(元素)位置删除 + v1.erase(v1.begin()); //在头部删除一个 + + //根据元素的值 + v1[1] = 2; + v1[3] = 2; + + vector::iterator it; + for(it = v1.begin(); it != v1.end(); it++){ + if(*it == 2){ + v1.erase(it); //删除的是元素,参数是迭代器 + } + } + + v1.insert(v1.begin(), 100); + v1.insert(v1.end(), 200); + printV(v1); + return 0; +} +``` + +## 二、deque的基本操作 + +双端数组的基本操作; + +代码如下: + +```cpp +#include +#include +#include +using namespace std; + +void printD(deque &d){ + deque::iterator it; + for(it = d.begin(); it != d.end(); it++){ + cout<<*it<<" "; + } + cout< d1; + d1.push_back(1); + d1.push_back(3); + d1.push_back(5); + + d1.push_front(-11); + d1.push_front(-33); + d1.push_front(-55); + + cout<<"头部元素:"<::iterator it; + it = find(d1.begin(), d1.end(), -33); + if(it != d1.end()){ + cout<<"-33数组的下标是:"< +#include +using namespace std; + +class Teacher{ + public: + void printS(){ + cout<<"age :"< s; + s.push(&t1); + s.push(&t2); + s.push(&t3); + + while(!s.empty()){ + Teacher *tmp = s.top(); + tmp->printS(); + s.pop(); + } + + return 0; +} + +/* +int main(void){ + Teacher t1,t2,t3; + + t1.age = 31; + t2.age = 32; + t3.age = 33; + + stack s; + s.push(t1); + s.push(t2); + s.push(t3); + + while(!s.empty()){ + Teacher tmp = s.top(); + tmp.printS(); + s.pop(); + } + + return 0; +} +*/ +/* +int main(void){ + stack s; + + //入栈 + for(int i = 0; i < 10; i++){ + s.push(i+1); + } + + cout< +#include +using namespace std; + +class Teacher{ + public: + int age; + char name[25]; + public: + void printQ(){ + cout<<"age :"< q; + q.push(&q1); + q.push(&q2); + q.push(&q3); + + while(!q.empty()){ + Teacher *tmp; + tmp = q.front(); //获取队列头的元素 + tmp->printQ(); + q.pop(); + } + + return 0; +} + + +/* +//队列中的基础数据类型, +int main(void){ + queue q; + + q.push(1); + q.push(2); + q.push(3); + + + cout<<"对头元素:"< +#include +using namespace std; + +int main(void){ + priority_queue p1; //默认的情况下:是最大优先级队列; + priority_queue, less > p2; + priority_queue, greater > p3; //是最小的优先级队列 + + p1.push(33); + p1.push(11); + p1.push(22); + p1.push(77); + p1.push(55); + p1.push(99); + + cout<<"队头元素:"< +#include +using namespace std; + +void printL(list &l){ + list::iterator it; + for(it = l.begin(); it != l.end(); it++){ + cout<<*it<<" "; + } + cout< +素的位置; +int main(void){ + list l; + + cout<<"list的大小:"<::iterator it = l.begin(); + it++; + it++; + it++; + l.insert(it, 100); //STL中的插入默认是前插; + printL(l); + + +//1、list链表的节点的index是从0位置开始的 +//2、insert方法都是默认的前插 + return 0; +} +``` + +### 6.2、list的删除 + +代码如下: + +```cpp +#include +#include +using namespace std; + + +void printL(list &l){ + list::iterator it; + for(it = l.begin(); it != l.end(); it++){ + cout<<*it<<" "; + } + cout< l; + + for(int i = 0; i < 10; i++){ + l.push_back(i); + } + + list::iterator it1 = l.begin(); + list::iterator it2 = l.begin(); + it2++; + it2++; + it2++; + + l.erase(it1, it2); //删除是左闭右开的操作;[0, 3) + printL(l); + + l.erase(l.begin()); + printL(l); + + l.insert(l.begin(), 100); + l.insert(l.begin(), 100); + l.insert(l.begin(), 100); + l.insert(l.begin(), 100); + l.insert(l.begin(), 100); + printL(l); + + l.remove(100); //删除元素的方法,删除了所有值为100的元素; + printL(l); + + return 0; +} +``` + +## 七、说明 + +原创文章链接:[C++进阶系列之STL(5)---vector,deque,list相关操作](https://mp.weixin.qq.com/s?__biz=MzUxMzkyNDk0Ng==&mid=2247484009&idx=1&sn=fc69d8e7a266b383193492791c2d11b1&chksm=f94c8854ce3b01426ae280b09852432458f7bd5cef93627f88024136533ccccdc456a08b2d6c&scene=21#wechat_redirect) diff --git "a/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_\344\273\277\345\207\275\346\225\260.md" "b/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_\344\273\277\345\207\275\346\225\260.md" new file mode 100644 index 0000000..d308369 --- /dev/null +++ "b/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_\344\273\277\345\207\275\346\225\260.md" @@ -0,0 +1,85 @@ +- [一、仿函数](#一仿函数) +- [二、仿函数和回调函数的区别](#二仿函数和回调函数的区别) +- [三、说明](#三说明) + +## 一、仿函数 + +- **本质:是一个对象,用起来像函数;** +- **原因:在类内对()进行了重载;** + +## 二、仿函数和回调函数的区别 + +代码如下: + +```cpp +#include +#include +#include +#include +using namespace std; + +//函数对象:类重载了(); +//函数对象和函数是很类似的; + +template +class ShowElem{ + public: + ShowElem(){ + n = 0; + } + void operator()(Type &t){ //重载了(),此时可以叫做函数对象; + n++; + cout< showElem; + showElem(a); //函数对象的()的执行,很像一个函数;所以又叫做仿函数; + //函数对象可以跟函数的调用一样,直接把对象当作函数名称来使用!!! +} + +//函数对象和普通函数的区别; +template //函数模版 +void FuncShowElem(Type &t){ + cout< v1; + v1.push_back(1); + v1.push_back(3); + v1.push_back(5); + + for_each(v1.begin(), v1.end(), ShowElem()); //匿名函数对象 匿名仿函数; + cout< + +## 三、说明 + +原创文章链接:[C++进阶系列之STL(8)---仿函数](https://mp.weixin.qq.com/s?__biz=MzUxMzkyNDk0Ng==&mid=2247484012&idx=1&sn=1b04e505a1e24a013551b664e4590a13&chksm=f94c8851ce3b0147baed71aa4c59b680bab6eb7a126d19d8deccfdd42b67c8829f3145a79b92&scene=21#wechat_redirect) diff --git "a/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_\345\207\275\346\225\260\351\200\202\351\205\215\345\231\250.md" "b/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_\345\207\275\346\225\260\351\200\202\351\205\215\345\231\250.md" new file mode 100644 index 0000000..d7e04c2 --- /dev/null +++ "b/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_\345\207\275\346\225\260\351\200\202\351\205\215\345\231\250.md" @@ -0,0 +1,48 @@ +- [一、绑定器和取反器](#一绑定器和取反器) +- [二、代码实现](#二代码实现) +- [三、说明](#三说明) + +## 一、绑定器和取反器 + +1. 绑定器:把二元函数对象中一个参数固定,使之转为一元函数,C++标准库中提供了2种预定义的binder适配器,bind1st、bind2nd,分别绑定了第一个/第二个参数; +2. 取反器:翻转的适配器; +3. 一元函数:只针对一个参数,只有一个返回值;二元函数:针对2个参数,只有一个返回值; +4. 绑定器针对的是参数,绑定后其值就不变了,取反器对其进行取反; +5. 可以通过调试工具-g;对源码进行剖析; + +## 二、代码实现 + +关于绑定器和取反器的代码: + +```cpp +#include +#include +#include +using namespace std; + +int main(void){ + int ar[] = {1,3,5,7,9,8,6,4,2,0}; + int n = sizeof(ar) / sizeof(int); + int nCount = count(ar,ar+n, 4);//求数组中一共有多少个4 + cout<(), 4));//用的是第二个绑定器进行的绑定;; + //4 < x + //nCount = count_if(ar, ar+sizeof(ar)/sizeof(int), bind1st(less(), 4));//用的是第一个绑定器进行的绑定; + cout<= 4的个数; + nCount = count_if(ar, ar+n, not1(bind2nd(less(), 4)));//用的是取反器进行取反; + cout< + +## 三、说明 + +原创文章链接:[C++进阶系列之STL(4)---函数适配器](https://mp.weixin.qq.com/s?__biz=MzUxMzkyNDk0Ng==&mid=2247484008&idx=1&sn=fb97ad8fe76fa88520fa6c2debb507b2&chksm=f94c8855ce3b01436d69e48755d940aa2f415b4b53c4e8ff6258a4a28ef9f05d1303e44dc07e&scene=21#wechat_redirect) diff --git "a/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_\345\211\226\346\236\220List\351\203\250\345\210\206\346\272\220\347\240\201.md" "b/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_\345\211\226\346\236\220List\351\203\250\345\210\206\346\272\220\347\240\201.md" new file mode 100644 index 0000000..6d76de8 --- /dev/null +++ "b/article/C++_STL/C++\350\277\233\351\230\266\347\263\273\345\210\227\344\271\213STL_\345\211\226\346\236\220List\351\203\250\345\210\206\346\272\220\347\240\201.md" @@ -0,0 +1,237 @@ +- [一、STL](#一stl) +- [二、list(双向循环链表)](#二list双向循环链表) +- [三、list部分源码实现剖析](#三list部分源码实现剖析) + - [3.1、list模型如下](#31list模型如下) + - [3.2、list的构造函数和析构函数](#32list的构造函数和析构函数) + - [3.3、list的迭代器](#33list的迭代器) +- [四、总结](#四总结) +- [五、说明](#五说明) + +## 一、STL + +**库函数的设计第一位是通用性,模板为其提供了可能;标准模板库中的所有算法和容器都是通过模板实现的。** + +**STL(标准模板库)是 C++最有特色,最实用的部分之一。** + +STL整个架构模型如下: + +
+ +## 二、list(双向循环链表) + +调用STL系统的 list,用系统的双向循环链表结构处理: + +```cpp +#include +#include //调用系统的list,双向循环链表结构 +using namespace std; + +int main(void){ + + list mylist; + for(int i = 1; i <= 10; i++){ + mylist.push_back(i); //接口,末尾增加 + } + list::iterator it = mylist.begin(); //迭代器, + while(it != mylist.end()){ + cout<<*it<<"-->"; //打印内部数字 + ++it; //每次往后移一个 + } + cout<<"NULL"< + +阅读其源代码,分析了部分的功能: + +```cpp +#ifndef _LIST_H //条件宏编译,避免重复定义 +#define _LIST_H + +#include //断言引入的头文件 +#include //申请空间所引入的头文件 + +template //此处先不涉及空间置配器 +class list{ //list类 +public: + struct _Node; + typedef struct _Node* _Nodeptr; //指向节点的指针类型 + struct _Node{ //_Node这个是节点类型 + _Nodeptr _Prev; //前驱节点 + _Nodeptr _Next; //后继节点 + _Ty _Value; //模板数据类型 + }; + struct _Acc{ //定义_Acc这个类型 + typedef struct _Node*& _Nodepref; //指向节点类型指针的引用 + typedef _Ty& _Vref; //这个数据类型的引用 + static _Nodepref _Next(_Nodeptr _P)//静态方法, 返回值是节点指针的引用 ,参数是指向节点的指针 + {return ((_Nodepref)(*_P)._Next);}//:*_P得到这个节点,()强制类型转换的优先级没有.高,所以此时先取_Next,在进行强制类型转换的工作,返回一个指向节点指针的引用。 + static _Nodepref _Prev(_Nodeptr _P) + {return ((_Nodepref)(*_P)._Prev);} + static _Vref _Value(_Nodeptr _P) + {return ((_Vref)(*_P)._Value);} + }; +public: //以下的类型是_A这个类下面的类型,_A这个类在空间置配器中定义 + typedef typename _A::value_type value_type; + typedef typename _A::pointer_type pointer_type; + typedef typename _A::const_pointer_type const_pointer_type; + typedef typename _A::reference_type reference_type; + typedef typename _A::const_reference_type const_reference_type; + typedef typename _A::size_type size_type; //这个类型其实就是size_t + +private: + _Nodeptr _Head; //指向头结点的指针 + size_type _Size; //有几个节点个数 +}; + +#endif +``` + +以上代码主要是struct _Acc这个类的理解好至关重要!!! + +### 3.2、list的构造函数和析构函数 + +```cpp +public: + explicit list():_Head(_Buynode()),_Size(0) //explicit显示调用此构造函数,给头一个指向,刚开始0个 + {} + ~list() + { //释放空间和空间配置器有关,在现阶段先不关心。 + erase(begin(), end()); //调用开始,结束函数释放空间; + _Freenode(_Head); //释放头; + _Head = 0, _Size = 0; //都赋空; + } + .................................................. +protected: + _Nodeptr _Buynode(_Nodeptr _Narg=0, _Nodeptr _Parg=0) // 返回值为节点指针类型,参数都为节点指针类型,传的应该是后继和前驱指针,默认都为0; + { + _Nodeptr _S = (_Nodeptr)malloc(sizeof(_Node));//申请一个节点空间,把地址给了_S; + + assert(_S != NULL); //所申请的空间存在的话 + _Acc::_Next(_S) = _Narg!=0 ? _Narg : _S; //给新生成的节点的_Next赋值 + _Acc::_Prev(_S) = _Parg!=0 ? _Parg : _S; //给新生成的节点的_Prev赋值 + return _S; //返回这个新生成节点的地址 + } +//这个_Buynode函数的意思是:当创建的是第一个节点时,自己一个节点连向自己,构成双向循环链表,其他的情况则是插入到两个节点之间!!! +........................................................ +``` + +### 3.3、list的迭代器 + +```cpp +public: + class iterator{ //迭代器也是一个类,是list的内部类; + public: + iterator() + {} + iterator(_Nodeptr _P):_Ptr(_P) + {} + public: + iterator& operator++(){ // ++it,前++的运算符重载 + _Ptr=_Ptr->_Next; //因为是链表结构,内部实现迭代器的++,是进行了++的重载;使其指针的移动到下一个节点; + return *this; //返回的是这个节点的引用。 + } + iterator operator++(int)// it++ + { + _It it(_Ptr); //先保存原先节点 + _Ptr = _Ptr->_Next; //移到下一个节点 + return it; //返回原先的; + } + iterator operator--(int); //类似 + iterator& operator--(); + reference_type operator*()const //对*的重载 + {return _Ptr->_Value;} //返回这个节点的_Value值 + pointer_type operator->()const //对->的重载 + //{return &_Ptr->_Value;} 自己实现的,->的优先级高于&,所以将_Value的地址返回 + {return (&**this);} //系统中的,this是迭代器的地址,*this是迭代器对象,再来一个*时,调用上面的(对*的重载),此时还是返回_Value的地址。 + public: + bool operator!=(const iterator &it)const //迭代器对象的比较 + {return _Ptr!=it._Ptr;} //比的是指向节点的指针; + public: + _Nodeptr _Mynode()const //得到当前节点的地址; + {return _Ptr;} + protected: + _Nodeptr _Ptr; //迭代器的数据成员是一个指向节点的指针。 + }; + typedef iterator _It; //_It 就是迭代器类型 +public: + iterator begin(){return iterator(_Acc::_Next(_Head));} //begin()函数得到头结点的后继(第一个有效节点的地址) + iterator begin()const; + iterator end(){return iterator(_Head);} //end()函数得到的是头结点(也就是最后一个节点的后继地址); +public: //前面的已经讲的很清楚了,后面的都是调用即可; + void push_back(const _Ty &x) + {insert(end(),x);} + void push_front(const _Ty &x) + {insert(begin(),x);} +public: + iterator insert(iterator _P, const _Ty &_X=_Ty()) + { + _Nodeptr _S = _P._Mynode(); //得到节点地址 + _Acc::_Prev(_S) = _Buynode(_S, _Acc::_Prev(_S)); //下面的三句调用前面的函数_Buynode()实现了插入功能; + _S = _Acc::_Prev(_S); + _Acc::_Next(_Acc::_Prev(_S)) = _S; + ++_Size; //个数加1 + return iterator(_S); + } + void insert(iterator _P, size_type _M, const _Ty &_X) //插入个数_M个,以下几个调用前面函数; + { + for(; 0<_M; --_M) + insert(_P,_X); + } + void insert(iterator _P, const _Ty *_F, const _Ty *_L) //区间的插入 + { + for(; _F!=_L; ++_F) + insert(_P, *_F); + } + void insert(iterator _P, _It _F, _It _L) //迭代器的插入 + { + for(; _F!=_L; ++_F) + insert(_P, *_F); + } + /* + void push_back(const _Ty &x) //尾随增加最后 + { + _Nodeptr _S = _Buynode(_Head, _Acc::_Prev(_Head)); //实现插入功能 + _Acc::_Value(_S) = x; + _Acc::_Next(_Acc::_Prev(_Head)) = _S; + _Acc::_Prev(_Head) = _S; + _Size++; //最后加1 + } + + iterator erase(iterator _P)// 删除空间 + { + _Nodeptr _S = (_P++)._Mynode(); + _Acc::_Next(_Acc::_Prev(_S)) = _Acc::_Next(_S); + _Acc::_Prev(_Acc::_Next(_S)) = _Acc::_Prev(_S); + + --_Size; //个数减少1个 + return _P; + } + iterator erase(iterator _F, iterator _L) //调用函数,删除区间 + { + while(_F != _L) + erase(_F++); + return _F; + } + void clear() //清除所有空间 + {erase(begin(), end());} + +#endif + +``` + +## 四、总结 + +1. **迭代器的本质有了了解,是一个内部类,它将是一个对象,内部数据成员是一个指向节点的指针;** +2. **迭代器对->的重载返回的是节点内部数据的地址,而不是节点的地址;** +3. **迭代器对每种数据结构的实现均不相同,(Stack, queue, list...........)** +4. **空间配置器:对所有的数据结构而言,只有一份,作用:申请,释放空间,构造,析构对象。** + +## 五、说明 + +原创文章链接:[C++进阶系列之STL(1)---剖析List部分源码](https://mp.weixin.qq.com/s?__biz=MzUxMzkyNDk0Ng==&mid=2247483978&idx=1&sn=b89b8bc5ff0fdb43c589c2e261aa0dce&chksm=f94c8877ce3b0161680d57a39d3797e15decbea573fd95661af64e213c374cf1b58cab212c65&scene=21#wechat_redirect)