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

stl_vector.h :
    vector:可随机访问元素的序列容器,从后插入或删除在常量时间内完成,从首部或中间则需线性时间内完成;

    _Vector_alloc_base:vector分配基类模板;模板参数分别为数据类型T,分配器类型_Allocator,以及一个bool标识_IsStatic(用于区分是否为标准分配器或SGI分配器);
    数据成员:
        _M_data_allocator:分配器对象;
        _M_start:保存申请的缓冲区首地址(等同于容器元素的首地址);
        _M_finish:保存容器内容长度时的尾地址;
        _M_end_of_storage:保存申请的缓冲区尾地址;
    成员函数:
        构造函数:分配器引用allocator_type类型以初始化_M_data_allocator;
        get_allocator:获取分配器对象_M_data_allocator;
        _M_allocate:通过分配器对象_M_data_allocator分配大小为n个的元素类型大小内存空间;
        _M_deallocate:释放指定数据元素类型指针地址大小为n个数据元素类型大小的内存空间;
    此外还提供特化版本_Vector_alloc_base<_Tp, _Allocator, true>,该分配模板基类内部不再使用分配器对象,而是直接使用simple_alloc的静态成员函数进行分配管理;

    _Vector_base:vector基类,继承于_Vector_alloc_base,其基类的模板参数_IsStatic则通过_Alloc_traits萃取获得的_S_instanceless来初始化;
        构造函数:重载版本,一种通过分配器类型对象初始化;另一种还增加了参数n,内部调用_M_allocate以预先分配n字节的缓冲区并调整_M_start、_M_finish、_M_end_of_storage值;
        析构函数:调用_M_deallocate释放当前缓冲区;

    vector:序列容器vector模板类,保护继承于_Vector_base,模板参数中的分配器使用宏__STL_DEFAULT_ALLOCATOR作为默认分配器;可通过调整宏设置,故而可使用allocator< T >或alloc
        (malloc_alloc(即__malloc_alloc_template<0>)或__default_alloc_template<__NODE_ALLOCATOR_THREADS, 0>)作为默认的内存分配器;
        此外重声明常规类型以及迭代器类型和常量迭代器类型(vector中迭代器类型与指针类型相同,不同于其他容器(如:list、deque等),其他容器需要单独提供相应迭代器);
        get_allocator:重写基类函数,获取基类的分配器对象;
        构造函数:多种重载版本的构造函数,包括提供分配的allocator_type参数、提供初始化元素个数n并以给定值value初始化或使用默认初始化值、提供数据元素首尾地址、
        (内部调用uninitialized_fill_n填充缓冲区元素)并调整__M_finish值;
        拷贝构造函数:同上初始化过程;
        析构函数:内部调用destroy销毁(不是释放)指定范围_M_start、_M_finish内存空间元素;
        begin:获取迭代器首部(返回_M_start);
        end:获取迭代器尾部(返回_M_finish);
        rbegin、rend:借助begin和end以初始化构造并返回反转迭代器对象;
        size:获取vector容量,也即迭代器范围(end()-begin());
        max_size:获取可容纳最大元素容量大小(size_type(-1)/sizeof(T));
        capacity:获取当前可容纳元素容量(size_type(_M_end_of_storage-begin()))(实际上即为当前容器已分配的内存空间大小);
        empty:判断容器是否为空(begin()==end());
        operator[]:返回指定索引下引用元素(*(begin()+n));
        at:功能同operator[],其会间接调用operator[],不过增加了边界检查,超过容器容量则抛出range_error异常;
        _M_allocate_and_copy:申请n字节大小内存空间并拷贝迭代器范围的原始元素数据至该空间,返回申请的内存空间首地址(迭代器);
        reserve:预订至少大于当前可容纳元素容量的n字节大小的内存空间(若小于或等于capacity则不做任何处理,否则调用_M_allocate_and_copy重申请内存空间,此后销毁原容器元素和内存空间并重新调整
        容器迭代器以及容量指针_M_end_of_storage为新申请的内存空间位置),故该操作可能导致大量的销毁和重新申请内存;
        assign:分配函数,提供两个重载版本,一个版本参数为size_type n, const T& val表示分配n个值为val的元素容器(调用_M_fill_assign填充分配),另一版本参数为一个输入迭代器范围(调用_M_assign_dispatch
        分配);
        _M_fill_assign:填充n个元素值为val的分配操作(若n大于capacity则需重新申请内存空间,不过其实现是通过重新申请n个元素值为val的vector对象并通过swap直接交换(事实上只是简单地交换了迭代器指针对象而已)
        ;若n小于capacity且大于size,则调用fill直接填充、uninitialized_fill_n初始化迭代器尾部后剩余的值并调整_M_finish尾迭代器即可;若n小于size,则先调用fill_n填充迭代器n个元素,调用erase销毁多余的
        内存空间);
        _M_assign_dispatch:分配分派模板,若迭代器对应元素类型为整型_Is_integer则调用_M_assign_dispatch模板参数为__true_type的版本(内部调用_M_fill_assign填充分配);否则调用_M_assign_aux分配,事实上该
        函数也有两个重载版本,针对输入迭代器或其子类类型和前向迭代器,之所以如此,因后者迭代器实现方式可更快速实现而不是输入迭代器的方式(当然后者采用前者的方式实现也可以,不过for遍历获取长度相对比较慢,
        如为随机迭代器等时会损失高效性(随机迭代器获取长度时可直接相减即可得到));
        _M_assign_aux:输入迭代器类型版本,通过直接将传参迭代器元素覆盖已存在的容器迭代位置容器元素,若输入参数迭代器元素比容器已有的少,则移除原容器多余元素;若输入参数迭代器还有多余则调用insert插入
        剩余的元素至容器中;
        _M_assign_aux:前向迭代器类型版本,先调用distance获取前向迭代器范围len,若len大于capacity则需重新申请内存空间,不过其实现是通过重新申请n个元素值为val的vector对象并通过swap直接交换(事实上只是简单地交换了迭代器指针对象而已)
        ;若len小于capacity且大于size,则调用advance获取first前向迭代器向前跳size时的迭代器mid,再调用copy拷贝first与mid迭代器范围数据至原容器、此后再调用uninitialized_copy初始化迭代器尾部后剩余的值并调整_M_finish尾迭代器;
        若len小于size,则调用copy拷贝first至last迭代器数据至原容器,再调用destroy销毁剩余多余原容器数据元素并调整_M_finish;
        front、back:获取容器首元素和尾元素(*begin()、*(end() - 1),若容器为空会出现运行时异常);
        push_back:容器尾部推入数据元素,若推入前还有足够缓冲区则直接在尾部迭代器_M_finish处调用construct初始化构造并调整_M_finish即可,否则调用_M_insert_aux在尾迭代器前插入数据元素并调整_M_finish;
        _M_insert_aux:在指定迭代器前插入数据元素x;若插入前还有足够缓冲区则调整尾迭代器+1并调用copy_backward后向拷贝元素数据,此后在插入位置直接覆盖原始数据元素即可;若不足则需要申请足够的内存空间,其策略为:若原容器容量为0,则
        申请1个元素空间即可,否则申请原始空间的2倍内存空间;接着调用uninitialized_copy拷贝原容器迭代器范围为首迭代器至插入迭代器点的数据元素至新内存空间中,此后在新内存空间的尾迭代器处调用construct初始化插入元素,再将剩下原容器的
        剩余部分的数据元素调用uninitialized_copy依次拷贝到后面,最后调用destroy、_M_deallocate释放容器所有元素和内存空间并调整原容器的迭代器为新的内存空间地址范围和容量即可;
        swap:交换两个vector容器对象(事实上内部只是简单地交换了迭代器指针而已);
        insert:提供三个重载版本;
            在指定迭代器前插入数据元素x并返回插入位置时的迭代器:
                若插入前还有足够缓冲区且在尾迭代器前插入则直接调用construct在尾迭代器_M_finish处初始化值x并调整_M_finish即可;否则调用_M_insert_aux实现具体的插入操作;
            在指定迭代器前插入迭代器范围内的所有元素:
                其调用_M_insert_dispatch并根据数据元素类型确定其参数_Is_integer是否为整型,若为整型则再调用_M_fill_insert进行插入填充,否则调用_M_range_insert插入,然而_M_range_insert为模板函数,
                具体调用哪个_M_range_insert也是根据数据元素迭代器类型,当为输入迭代器时则遍历输入迭代器循环调用insert插入数据(在遍历插入过程中需调整插入点迭代器位置),当为前向迭代器时采用的策略
                为:先调用distance获取前向迭代器范围len,此时可能的出现容器可用剩余内存不足的情况;
                    1. 若len小于剩余可用内存时则又分为两种情况(之所以有两种情况是为了尽可能提高插入效率(尤其是数据元素为整型等内置类型时));
                        1). 插入点到尾迭代器长度比插入迭代器范围大:
                            调用uninitialized_copy拷贝构造尾迭代器前n个元素时范围内数据至尾迭代器位置处并调整容器尾迭代器位置;
                            调用copy_backward将插入处迭代器至插入迭代器后未被拷贝的范围元素后向拷贝至原尾迭代器处;
                            调用copy将输入迭代器范围元素拷贝到插入点处;
                        2). 插入点到尾迭代器长度不比插入迭代器范围大:
                            获取尾迭代器至插入点迭代器的范围长度__elems_after;
                            调用advance获取输入迭代器首迭代器位置向后移动__elems_after时的位置mid;
                            调用uninitialized_copy拷贝构造mid至输入迭代器尾迭代器范围数据至容器尾迭代器位置并调整容器尾迭代器位置;
                            调用uninitialized_copy拷贝构造插入点迭代器至容器原尾迭代器范围数据至新容器迭代器位置处并调整容器尾迭代器位置;
                            调用copy将输入迭代器范围元素拷贝到插入点处;
                    2. 可用剩余内存不足时:
                        获取原容器大小并与输入迭代器范围长度中取最大值加上原容器大小作为新的将申请的容器缓冲区大小,申请新的缓冲区;
                        调用uninitialized_copy拷贝构造容器首迭代器至插入点迭代器范围数据至新的缓冲区首迭代器处并调整记录尾迭代器;
                        调用uninitialized_copy拷贝构造输入迭代器范围数据至新的缓冲区尾迭代器处并调整新的缓冲区尾部迭代器;
                        调用uninitialized_copy拷贝构造原容器插入点迭代器至原容器尾迭代器范围数据至新缓冲区尾迭代器处;
                        调用destroy、_M_deallocate销毁原容器元素和内存空间,调用容器各迭代器为新缓冲区的相应迭代器;
            在指定迭代器前插入n个数据元素x:
                调用_M_fill_insert填充插入,策略同_M_range_insert的前向迭代器类型的版本;
        pop_back:弹出容器尾部元素(前向移动尾迭代器并调用destroy销毁尾部元素);
        erase:两个重载版本,移除指定迭代器位置的元素版本(个人觉得有BUG,传入迭代器位置不能超过尾迭代器前一个否则会出现pop_back现象)调用copy直接将移除点迭代器后至尾迭代器拷贝至移除点前一个迭代器处;
            前向移动尾迭代器并调用destroy销毁尾部元素返回移除点迭代器;移除迭代器范围版本类似;
        resize:重置容器大小,提供两个重载版本;带初始化参数值版本中若需求容器大小new_size小于原容器大小,则调用earse移除容器首迭代器后new_size开始的迭代器至尾迭代器范围的数据元素并调整尾迭代器位置;
                否则调用insert在原容器尾迭代器处插入容器不足new_size大小的值为x的数据元素;
                不带初始化参数值版本,通过数据元素类型的默认构造函数以调用resize带参参数值版本;
        clear:调用erase移除容器数据元素;
        此外重载了多个不同的比较操作符以支持比较运算;
        重载赋值操作符operator=:以支持赋值操作,内部也采用了尽可能加快效率的操作;
    vector若采用默认带内存池的版本分配器时,若超过128字节将直接采用malloc、free管理内存,一般情况下vector需求内存空间比较大的情况下时内存池的意义将变得不大,尤其是某些vector成员函数的操作更是频繁申请、
    销毁内存空间,故线程池的意义主要在于减少内存碎片和频繁申请小内存空间的功能;当然若使用者提供自定义内存分配器(内存池)也可以自主管理策略;此外因为vector实际分配到的内存为连续的,故可以直接取迭代器或
    取指针或operator[]取地址也是安全的;

原文地址:https://www.cnblogs.com/haomiao/p/11647245.html

时间: 2024-11-03 20:56:51

SGI-STL简记(六)-序列容器(vector)的相关文章

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

stl_deque.h : deque:一种具有双端插入和删除,可随机访问元素的容器,从首部或后插入或删除在常量时间内完成,从中间则需线性时间内完成: __deque_buf_size:获取队列节点缓冲区大小(工具函数),当数据元素类型字节size小于512时则为512/size,否则为1,(意味着节点容器上限为512字节或者是一个自定义类型元素大小的字节): _Deque_iterator:专用于deque容器的迭代器模板类:重声明常规类型以及迭代器类型和常量迭代器类型,迭代器分类为rando

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

stl_list.h : list:一个可从任意位置快速插入和删除元素的双向链表,可在常数时间内完成,但是取数据.查找等则需要线性时间: _List_node_base:链表节点基类struct,仅包含_M_next._M_prev成员,其分别为指向当前节点基类类型的下一个.上一个节点的指针: _List_node:节点模板类,继承于_List_node_base,只是增加了一个数据成员_M_data,用以保存实际的node节点数据: _List_iterator_base:链表迭代器基类,所属

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:节点后拼接函数

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

stl_bvector.h : bit_vector 重声明为vector<bool, alloc>即使用alloc作为内存分配器,其为非模板类,__BVECTOR也即vector<bool, _Alloc>为模板类,若使bit_vector重声明为__BVECTOR则也可为模板类: 相比vector,其可保持一个位一个元素而不是至少一个字节一个元素:此外基本的函数和vector相同,还有其他额外的提供的接口实现: bit_vector一般被认为是非模板类: _Bit_refere

C++线性序列容器&lt;vector&gt;简单总结

C++线性序列容器<vector>简单总结 vector是一个长度可变的数组,使用的时候无须声明上限,随着元素的增加,Vector的长度会自动增加:Vector类提供额外的方法来增加.删除元素,比数组操作高效. 头文件:#include <vector> 命名空间:using namespace std:vector 构造函数 vector<int>vec_int;         // 创建一个整形元素的vector vector<string>vec_s

STL源码--序列容器(一)

一.vector.list.deque 迭代器 vector 原始指针 list  随机迭代器 deque   自定义迭代器,可以+n操作 迭代器失效 vector 插入删除在插入删除点后的均会失效(不包括末尾:插入如果重新分配的话就会整体失效:清楚所有也会所有失效 list  除了删除的点会失效其他的都不失效 deque   除了末尾和首端,均会失效 底层存储 vector 类似于数组连续存放 list  随机存放 deque   伪连续存储,有一个map控制各个缓冲区 二.stack.que

C++中防止STL中迭代器失效——map/set等关联容器——vector/list/deque等序列容器—如何防止迭代器失效—即erase()的使用

序列性容器::(vector和list和deque) erase迭代器不仅使所有指向被删元素的迭代器失效,而且使被 删元素之后的所有迭代器失效,所以不能使用erase(iter++)的方 式,但是erase的返回值为下一个有效的迭代器,所以   正确方法为:: for( iter = c.begin(); iter != c.end(); ) iter = c.erase(iter); 关联性容器::(map和set比较常用) erase迭代器只是被删元素的迭代器失效,但是返回值为void, 所

STL:序列式容器vector总结

说起数组我们都不陌生,但在C++中,除了数组还多了一个"新朋友"那就是vector.其实vector本质上与array的数据安排以及操作方式也其为相似.它俩唯一的差别就是空间灵活性. 无论在C语言还是C++中,array的空间一旦申请完成就不能进行更改,如果需要更大空间来存储数据,便得重新申请一个新的数组并将原来的数值拷贝过去,然后再将原来数组释放,而这一切都需要用户自己完成.而vector不同的是,它的空间分配更加灵活,当他内存不够存放时,它内部机制会自行进行容量扩充,这对于程序员来

《STL源码剖析》——第四章、序列容器

 1.容器的概观与分类 所谓序列式容器,其中的元素都可序(ordered)[比如可以使用sort进行排序],但未必有序(sorted).C++语言本身提供了一个序列式容器array,STL另外再提供vector,list,deque,stack,queue,priority-queue 等等序列式容器.其中stack和queue由于只是将 deque 头换面而成,技术上被归类为一种配接器(adapter).  2.vector vector的数据安排以及操作方式,与array非常相似.两者的唯一