SGI-STL简记(六)-序列容器(slist)

stl_slist.h :
    slist:单链表模板容器,;
    
    _Slist_node_base:单链表基类,只一个指向_Slist_node_base类型的_M_next成员指针,以表示指向下一个node节点;
    
    一些辅助工具函数:
        __slist_make_link:在指定节点prev_node后插入new_node节点,以加入构成链表;
        __slist_previous:找到指定节点的前一个节点(需要从节点head开始遍历查找节点链表);
        __slist_splice_after:节点后拼接函数,两个重载版本;
            一种是将__before_first节点以后的节点至__before_last节点(含)拼接插入到pos指定的节点链表后的链表中;
            另一种是将给予一个__head链表头,将整个链表拼接插入到指定pos节点链表后的链表中;__slist_previous(head,0)表示获取尾节点;
        __slist_reverse:链表节点翻转;需要知道3个连续的节点标识便可实现翻转,然后依次遍历整个链表节点;
        __slist_size:遍历链表统计链表长度;
    
    _Slist_node:单链表节点模板类,继承于_Slist_node_base,只是增加了数据成员_M_data;
    
    _Slist_iterator_base:单链表迭代器基类;只是重声明了size_type、difference_type、iterator_category,为前向迭代器类型forward_iterator_tag;
    有一个数据成员_Slist_node_base类型的_M_node指针用以表示当前迭代器所指向的节点;
    构造函数为_Slist_node_base类型以初始化_M_node成员;
    _M_incr:迭代器下移(后移),以指向单链表当前节点的下一个节点(单链表无前移操作);
    此外还重载了operator==、operator!=,内部直接比较_M_node值(即节点地址)表示迭代器是否相等;
    
    _Slist_iterator:单链表迭代器模板类,继承于_Slist_iterator_base,内部重声明了常规类型以及迭代器、常迭代器类型;
    多个版本的构造函数,其中有参数类型_Slist_node以初始化基类中的_M_node、空迭代器、以及迭代器类型的参数;
    此外重载了解引用、取指针操作符(返回为当前迭代器_M_node(需先强制转化为子类类型_Slist_node)中的数据成员_M_data,因_Slist_node兼容_Slist_node_base类型内存布局,故可强制C类型转化);
    重载了operator++、operator++(int)实现迭代器自增操作,内部调用基类的_M_incr实现;
    
    _Slist_alloc_base:单链表模板分配基类,模板参数分别为数据类型T,分配器类型_Allocator,以及一个bool标识_IsStatic(用于区分是否为标准分配器或SGI分配器);
    数据成员:
        _M_head:当前单链表的头节点指针,其还用来作为迭代器的首、尾判断依据(哨兵),可以更方便实现而且空间浪费也不多就一个_Slist_node_base节点的大小;;
    成员函数:
        构造函数:分配器引用allocator_type类型以初始化_M_node_allocator;
        get_allocator:获取分配器对象_M_node_allocator;
        _M_get_node:通过分配器对象_M_node_allocator分配大小为1个的数据元素类型的内存大小空间
        _M_put_node:释放指定数据元素类型指针地址大小为1个数据元素类型大小的内存空间;
    此外还提供特化版本_Slist_alloc_base<_Tp, _Allocator, true>,该分配模板基类内部不再使用分配器对象,而是直接使用simple_alloc的静态成员函数进行分配管理;
    
    _Slist_base:单链表模板基类,继承于_Slist_alloc_base,其基类的模板参数_IsStatic则通过_Alloc_traits萃取获得的_S_instanceless来初始化
        构造函数:通过分配器类型对象初始化基类分配器并初始化_M_head._M_next为空;
        析构函数:调用_M_erase_after销毁头节点以后的各节点缓冲区;
        _M_erase_after:两个重载版本,一个是移除指定节点后的一个元素,另一个为移除指定节点范围内的所有节点元素(不包结束节点);销毁节点内部调用destroy、
        _M_put_node析构并销毁内存空间;
        
    slist:序列容器slist链表模板类,保护继承于_Slist_base,模板参数中的分配器使用宏__STL_DEFAULT_ALLOCATOR作为默认分配器;可通过调整宏设置,故而可使用allocator< T >或alloc
        (malloc_alloc(即__malloc_alloc_template<0>)或__default_alloc_template<__NODE_ALLOCATOR_THREADS, 0>)作为默认的内存分配器;
        此外重声明常规类型以及迭代器类型和常量迭代器、翻转迭代器等类型;
        get_allocator:重写基类函数,获取基类的分配器对象;    
        构造函数:提供多个重载版本,包括提供分配的allocator_type参数、提供初始化元素个数n并以给定值value初始化或使用默认初始化值、提供输入迭代器范围(调用_M_insert_after_range
        在容器尾部插入输入迭代器范围数据或者插入n个元素数据);
        _M_create_node:创建节点缓冲区并初始化该节点值为指定值,内部先调用_M_get_node申请元素类型大小的缓冲区,此后调用_Construct初始化构造当前节点值;
        assign:分配函数,重载版本,分别提供分配n个值为val的初始化容器以及分配输入迭代器范围的初始化容器;前者调用_M_fill_assign填充分配;后者则调用_M_assign_dispatch模板函数分配;
        _M_fill_assign:其策略为若原容器有数据则通过for循环赋值拷贝数据,若不足指定大小的容器时则调用_M_insert_after_fill在后面插入填充剩余个数的数据,否则调用_M_erase_after移除
        原容器多余的数据元素;
        _M_assign_dispatch:分配分派模板函数,根据数据元素类型调用相应版本,当为_Is_integer类型时则调用_M_fill_assign填充即可,否则处理方式为若原容器有数据则通过while循环赋值拷贝数据,
        若不足指定大小的容器时则调用_M_insert_after_range在后面插入填充剩余迭代器范围的数据元素,否则调用_M_erase_after移除原容器多余的迭代器范围的数据元素,类似于_M_fill_assign的处理策略;
        此外该实现与operator=赋值拷贝类似;
        _M_insert_after_fill:在指定pos或插入后的pos后插入指定个数n值为val的数据元素;内部for循环调用_M_create_node、__slist_make_link构造并插入节点;
        _M_insert_after_range:类似于_M_insert_after_fill,只是取各迭代器值初始化插入节点,内部while循环调用_M_create_node、__slist_make_link构造并插入节点;
        begin、end:获取容器首部、尾部元素的迭代器,其中首部迭代器为_M_head._M_next构造,尾部迭代器以空构造即可;
        before_begin:获取容器首部迭代器前一节点的迭代器,事实上返回为_M_head节点构造的迭代器;
        size:获取容器元素个数,调用__slist_size遍历累计容器元素;
        max_size:获取容器容量,返回size_type(-1);
        empty:判断容器是否为空,一般优先采用empty而不是size来判断容器是否为空,因size可能会比较耗时;
        swap:交换容器元素,事实上只需要交换容器的头节点的下一个节点指向即可;
        front:获取容器首元素;返回值为((_Slist_node*)_M_head._M_next)->_M_data;
        push_front:采用首插法实现首部插入;内部在_M_head节点后调用_M_create_node、__slist_make_link构造节点并拼接至容器;
        pop_front:移除首元素,内部调整头_M_head节点指向下一个节点的位置,对于移除的元素则调用destroy、_M_put_node析构并销毁内存空间;
        previous:查找指定节点的前一个节点的迭代器;内部调用__slist_previous遍历查找;
        insert_after:在指定位置后插入节点,有多个重载版本;
            一种为在指定迭代器位置后插入指定值val,内部调用_M_insert_after实现;
            对于插入n个值为val的重载版本则调用_M_insert_after_fill插入填充;
            此外参数为输入迭代器范围的重载版本则调用_M_insert_after_range插入填充;
        insert:类似于insert_after,有多个重载版本,不过是在指定位置前面插入,内部采用调用__slist_previous获取到指定位置的前一个节点的迭代器实现前向插入;
        因为其内部调用__slist_previous获取前一个节点,故效率是比较差的;
        erase_after:移除指定位置后或迭代器范围内的容器元素;内部调用_M_erase_after实现;
        erase:移除指定位置或迭代器范围的容器元素,同erase_after类似;不过其内部调用__slist_previous获取当前节点的前一个节点的迭代器并调用_M_erase_after以达到移除当前节点位置的目的;
        resize:提供两个重载版本,重置容器空间容量大小,内部先通过while循环得到需重置的剩余大小,若该值不为0,则调用_M_erase_after移除其后剩余容器元素;否则调用_M_insert_after_fill
        迭代器尾部插入不足重置大小时剩余的值;
        clear:清空容器元素(调用基类_M_erase_after清空head节点后的所有容器元素节点);
        splice_after:拼接迭代器范围数据、某个迭代器位置或某个容器的所有的数据至当前容器指定位置后;内部调用__slist_splice_after实现;
        splice:同splice_after,不过内部调用__slist_previous获取当前节点的前一个节点以拼接;
        reverse:翻转容器元素,内部调用__slist_reverse实现翻转;
        remove:移除指定容器内指定元素;为了达到相对高效,内部获取while循环遍历链表节点,取各个节点的下一个节点的值并调用_M_erase_after移除;
        remove_if:移除满足指定条件的自定义函数的元素,即自定义函数返回值为true时的容器元素值将被移除;
        unique:移除相邻相等元素,同remove类似实现机制,内部调用_M_erase_after移除相等的节点;此外还提供了外部自定义比较函数的重载版本;
        merge:归并合并,使用首插法移除源容器元素并归并于当前容器中且有序排序(前提是两个容器元素已有序)(默认为升序),(借助于__slist_splice_after),此外提供外部提供比较函数的重载版本,
        事实上该__slist_splice_after只是循环拼接一个元素至容器;
        sort:排序容器元素,默认为升序,也提供了外部自定义比较函数实现排序;实现策略类似于二路归并排序的思想,并且与list中的sort实现几乎也一样:
        
        此外重载了各种比较操作符,而其中最重要的是operator==为遍历依次比较两个容器的元素,operator<调用lexicographical_compare依次遍历比较容器元素;
        
    slist若采用默认带内存池的版本分配器时,若超过128字节将直接采用malloc、free管理内存,一般情况下需求内存空间比较大的情况下时分配器意义不大基本上是直接采用malloc、free管理,容器内部缓冲区
    结构不同于list,其为各个节点只有指向下一个节点的指针,相对节省了内存占用,因无指向上一个节点的指针,故部分函数实现比较耗时或者根本难以实现,迭代器类型也不同,slist为前向迭代器;
    而list为双向迭代器类型;一般情况下slist比较节省内存占用部分操作也比较快速,当然有的操作却不尽人意,slist和list该使用哪个可根据实际需求。
   此外,C++11提供了std::forward_list单向(前向)列表模板类实现。

 

posted @ 2019-10-10 12:32  浩月星空  阅读(342)  评论(0编辑  收藏  举报