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_reference:位引用类,作为位迭代器类和bit_vector类中的引用类型;
    数据成员,均为unsigned int类型或指针类型(后面为了便于分析,暂且定为4字节大小):
        _M_p:指向缓冲区中存储该位码值所在的地址;
        _M_mask:当前位掩码值(事实上目前一般情况下该值始终为仅含一个1其余均为0的二进制形式下的值),也即_M_p所指向的4字节位码中相应掩码值中为1的位置即为当前引用类表示的位值;
    成员函数:
        构造函数初始化位码值地址和掩码值;
        operator bool:重载bool转换,内部实现为将位码值和掩码值按照位取与的结果;
        operator=:重载赋值拷贝运算符,内部实现为为拷贝参数值为true,则调整位码值为当前位码值与掩码值按位或的结果,否则为当前位码值与掩码值取反后再与其求与的结果;
        事实上也就是将位码值所在的缓冲区下所对应掩码值位置的值修改为1或者是0;
        operator==:重载比较相等,均位引用对象以转化为bool后比较;
        operator<:仅当当前类对象为false且参数对象为true时为ture,否则均为false;
        flip:调整位码值为当前位码值与掩码值按位异或的结果
    一个工具函数:swap交换两个位引用对象,此外__WORD_BIT为字长或32或64,依赖于机器;    

    _Bit_iterator_base:位迭代器模板基类,继承于random_access_iterator<bool, ptrdiff_t>,这样就具有随机访问方式以及各重声明的常规类型;
    数据成员:
        _M_p:当前迭代器所指向的缓冲区存储数据节点(字长大小)的地址;
        _M_offset:位码偏移量,即当前迭代器所指代的位码值位置索引偏移(一般值范围为0~31或0~63,依据机器字大小决定),该值与_M_mask区别就是,_M_mask = 1 << _M_offset;
    成员函数:
        构造函数初始化_M_p和_M_offset;
        _M_bump_up:迭代器后移(上提);若当前位码偏移量_M_offset已达到了字长,则调整_M_p指向下一个字长的位置且设置偏移量值为0,否则位码偏移量自加1即可;
        _M_bump_down:迭代器前移(下拉):若当前位码偏移量为0,则调整_M_p指向上一个字长的位置且设置偏移量值为字长大小-1,否则位码偏移量自减1即可;
        _M_incr:迭代器后移或前移i位;计算调整_M_p和_M_offset;
        此外还有多个重载的比较运算符,其中operator==则是当所指向地址和位码偏移量均相等时才相等;operator<则是所指向地址小于或者当所指向地址相等而位码偏移量小于时,
        其任意一条件满足则为true,否则为false;
    operator-:计算两个迭代器间的位码差值;

    _Bit_iterator:位迭代器类,继承于_Bit_iterator_base;内部通过_Bit_reference重声明其引用类型、指针类型;
        构造函数初始化基类_M_p和_M_offset;
        重载了解引用operator*操作符,返回对_Bit_reference位引用类对象的封装(_Bit_reference(_M_p,1<<_M_offset));
        重载了自增operator++、operator++(int)以及自减operator--operator--(int):内部调用基类的_M_bump_up或_M_bump_down实现;
        重载了operator+=、operator-=、operator+、operator-(返回迭代器对象)以及operator[]操作符(返回_Bit_reference位引用对象);内部调用基类的_M_incr实现;

    _Bit_const_iterator    :常位迭代器类,其他实现同_Bit_iterator且构造函数多了一个拷贝构造函数允许从其他位迭代器对象构造;

    _Bvector_alloc_base:bit_vector分配基类模板;模板参数分别为分配器类型_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_bit_alloc:通过分配器对象_M_data_allocator分配大小为(n + __WORD_BIT - 1)/__WORD_BIT)个的bool类型大小内存空间,保证按字对齐,即至少会分配一个字的大小;
        _M_deallocate:释放_M_start指针地址且大小为_M_end_of_storage - _M_start._M_p的内存空间;
    此外还提供特化版本_Bvector_alloc_base<_Tp, _Allocator, true>,该分配模板基类内部不再使用分配器对象,而是直接使用simple_alloc的静态成员函数进行分配管理;

    _Bvector_base:    bit_vector基类模板,继承于_Bvector_alloc_base,内部重声明了基类类型、分配器类型;
        构造函数初始化基类分配器;
        析构函数调用基类的_M_deallocate释放申请的内存缓存空间;

    vector<bool, _Alloc>:继承于_Bvector_base<_Alloc>,该模板参数_Alloc可由用户外部提供,也可以使用内置的分配器,在typedef vector<bool, alloc>bit_vector时,此bit_vector默认使用的分配器为alloc;
    此外其重声明了常规类型以及由位引用作为引用和指针类型,迭代器和常迭代器由_Bit_iterator、_Bit_const_iterator重声明,还有反向迭代器包装类型;
        get_allocator:重写基类函数,获取基类的分配器对象;
        构造函数:多种重载版本的构造函数,包括提供分配的allocator_type参数、提供初始化元素个数n并以给定值value初始化或使用默认初始化值、提供数据元素迭代器范围、
        (内部调用_M_initialize、fill或者调用_M_initialize_dispatch分配_M_initialize_range初始化区间、申请缓冲区并填充缓冲区元素);
        拷贝构造函数:内部调用_M_initialize初始化缓冲区并调用copy拷贝数据至缓冲区,之所以可以使用fill或copy是因为bool或位码为内置POD类型不用初始化构造;此外当val为true时,
        填充值为~0,即二进制的全1;
        _M_initialize:内部调用_M_bit_alloc分配足够容纳n个位码的字长缓冲区,_M_end_of_storage保存申请到的缓冲区结束位置,_M_start保存缓冲区首地址以及初始化内部偏移量为0,
        此外_M_finish为保存有效数据后的尾部迭代器;
        _M_initialize_dispatch:分配初始化模板,依据输入迭代器类型调用不同的实现,当为_Is_integer时则调用_M_initialize、fill即可,否则调用_M_initialize_range初始化;
        _M_initialize_range:初始化范围,为重载模板函数;
            当输入参数为输入迭代器或子类迭代器时,预先使首尾迭代器、缓冲区结束位置为空,此后遍历输入迭代器范围依次调用push_back插入各数据位元素;
            当输入参数为前向对象迭代器时,内部先调用distance获取输入迭代器范围长度,此后调用_M_initialize申请缓冲区并调用copy直接拷贝数据到缓冲区中;
        其他与vector类似的接口功能类似,需要注意的是vector返回的引用可以取地址安全使用,而bit_vector返回的引用为位引用对象不可直接取地址使用,除非知道内存数据布局方式;
        以下对新增加的接口和特殊处理的接口分析;
        flip:位码开关,即该接口可将缓冲区中的位码若为0则变为1,若为1则变为0;内部采用for循环处理所有以字为单位直接取反达到各个位取反的目的;
        capacity:获取容器可容纳位码的容量大小(返回const_iterator(_M_end_of_storage, 0) - begin()的差值);
        erase:重载版本的移除指定位置的元素或迭代器范围的元素,其实现方式比较简单,直接调用copy移动拷贝并调整_M_finish位置即可;
        _M_insert_aux:在指定迭代器位置处插入指定val;若插入时后面还有多余缓冲区未被使用,则调用copy_backward后向拷贝数据移动一个位码,此后直接对插入位置赋值并调整_M_finish
        即可,否则若当前容器容量为0则调用_M_bit_alloc分配一个字长的缓冲区或者为当前容器容量的2倍空间的缓冲区,此后调用copy拷贝原容器缓冲区数据至新缓冲区,插入点位置则直接赋值,
        而对于原容器余下的插入点位置后的数据再次调用copy拷贝,最后调用_M_deallocate释放原容器缓冲区并调整_M_end_of_storage、_M_start、_M_finish位置;
        可能重新分配内存的操作:insert、push_back、赋值拷贝、reserve、resize;而earse、pop_back、clear等则不会重分配内存;

    bit_vector因其类似于vector,部分功能和接口基本一样,此外其实际分配到的也是内存为连续的,但直接取迭代器或取指针或operator[]取地址是不可以直接使用的,因为返回的实际上为一个
    称之为位引用的对象,其内部包含了多个位的值,若结合掩码值也可以获取到正确的相应位的值,此外若使用者对内存布局了解的话则还可以获取到其他索引位的位值;
    另外bit_vector采用alloc默认带内存池的版本分配器时的确很多时候还是可以减少内存碎片和频繁申请的,当然若超过128字节将直接采用malloc、free管理内存;
    相对vector,更节省内存占用,因其一个元素只需要一个bit即可,即可节省约8倍的空间占用,此外因引入了位引用导致部分接口实现效率要差一点儿;
    bit_vector的申请内存布局格式为(以32位字长为例):
        31 30 29 28 .... 2 1 0 | 63 62 61 .... 34 33 32 | 95 .......
        对应数字表示容器索引位置下其在内存中的所在位置,字长位置的首地址并不是容器首元素的地址;
        如bit_vector V(4);
        V[0] = true;
        V[1] = true;
        V[2] = false;
        V[3] = true;
        二进制布局为:m_Ptr: 0000000....1011  对应十进制为11;当取V[3]时即(*m_ptr)&0000...1000,取V[1]时即(*m_ptr)&0000...0010;
        

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

时间: 2024-11-12 00:48:51

SGI-STL简记(六)-序列容器(bit_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简记(六)-序列容器(vector)

stl_vector.h : vector:可随机访问元素的序列容器,从后插入或删除在常量时间内完成,从首部或中间则需线性时间内完成: _Vector_alloc_base:vector分配基类模板:模板参数分别为数据类型T,分配器类型_Allocator,以及一个bool标识_IsStatic(用于区分是否为标准分配器或SGI分配器); 数据成员: _M_data_allocator:分配器对象; _M_start:保存申请的缓冲区首地址(等同于容器元素的首地址): _M_finish:保存容

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

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

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

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

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

STL源码剖析—序列容器

对于STL中的容器,存在一定的内含关系,例如,heap内含一个vector,priority-queue内含一个hep,stack和queue都含有一个deque,set/map/multiset/multimap都内含一个RB-tree,hash_x都内含一个hashtable. 对于序列容器来说,vector和list的插入都是在指向迭代器之前进行插入.同时需要注意的就是,对于vector来说,调用erase()函数也就是清除了几个连续空间上的元素,调用其析构函数,并没用释放原来元素占用的内

SGI STL源码stl_bvector.h分析

前言 上篇文章讲了 STL vector 泛化版本的实现,其采用普通指针作为迭代器,可以接受任何类型的元素.但如果用来存储 bool 类型的数据,可以实现功能,但每一个 bool 占一个字节(byte),而一个字节有 8 位(bit),这样就有点浪费了.所以 SGI STL 设计了一个特化版本的位向量容器 bit_vector 来节省空间内存.bit_vector 是一个 bit 位元素的序列容器,具有 vector 容器一样的成员函数,常用于硬件端口的控制. 原文地址:https://www.

STL中的set容器的一点总结2

http://blog.csdn.net/sunshinewave/article/details/8068326 1.关于set C++ STL 之所以得到广泛的赞誉,也被很多人使用,不只是提供了像vector, string, list等方便的容器,更重要的是STL封装了许多复杂的数据结构算法和大量常用数据结构操作.vector封装数组,list封装了链表,map和set封装了二叉树等,在封装这些数据结构的时候,STL按照程序员的使用习惯,以成员函数方式提供的常用操作,如:插入.排序.删除.