标准非STL容器 : bitset
1. 概念
什么是“标准非STL容器”?标准非STL容器是指“可以认为它们是容器,但是他们并不满足STL容器的所有要求”。前文提到的容器适配器stack、queue及priority_queue都是标准非STL容器的一部分。此外,valarray也是标准非STL容器。
bitset:一种高效位集合操作容器。
2. API
bitset提供的api:
(constructor)    Construct bitset (public member function)
operator[]    Access bit (public member function)
set    Set bits (public member function)
reset    Reset bits (public member function )
flip    Flip bits (public member function)
to_ulong    Convert to unsigned long integer (public member function)
to_string    Convert to string (public member function)
count    Count bits set (public member function)
size    Return size (public member function)
test    Return bit value (public member function )
any    Test if any bit is set (public member function)
none    Test if no bit is set (public member function)
3. 源码剖析
SGI bitset部分实现源码
- template<size_t _Nb>
 - class bitset : private _Base_bitset<__BITSET_WORDS(_Nb)>
 - {
 - private:
 - typedef _Base_bitset<__BITSET_WORDS(_Nb)> _Base;
 - typedef unsigned long _WordT;
 - private:
 - void _M_do_sanitize() {
 - _Sanitize<_Nb%__BITS_PER_WORD>::_M_do_sanitize(this->_M_hiword());
 - }
 - .....
 - }
 
- #define __BITS_PER_WORD (CHAR_BIT*sizeof(unsigned long))
 - #define __BITSET_WORDS(__n) \
 - ((__n) < 1 ? 1 : ((__n) + __BITS_PER_WORD - 1)/__BITS_PER_WORD)
 
- template<size_t _Nw>
 - struct _Base_bitset {
 - typedef unsigned long _WordT;
 - _WordT _M_w[_Nw]; // 0 is the least significant word.
 - _Base_bitset( void ) { _M_do_reset(); }
 - _Base_bitset(unsigned long __val) {
 - _M_do_reset();
 - _M_w[0] = __val;
 - }
 - static size_t _S_whichword( size_t __pos )
 - { return __pos / __BITS_PER_WORD; }
 - static size_t _S_whichbyte( size_t __pos )
 - { return (__pos % __BITS_PER_WORD) / CHAR_BIT; }
 - static size_t _S_whichbit( size_t __pos )
 - { return __pos % __BITS_PER_WORD; }
 - static _WordT _S_maskbit( size_t __pos )
 - { return (static_cast<_WordT>(1)) << _S_whichbit(__pos); }
 - _WordT& _M_getword(size_t __pos) { return _M_w[_S_whichword(__pos)]; }
 - _WordT _M_getword(size_t __pos) const { return _M_w[_S_whichword(__pos)]; }
 - _WordT& _M_hiword() { return _M_w[_Nw - 1]; }
 - _WordT _M_hiword() const { return _M_w[_Nw - 1]; }
 - void _M_do_and(const _Base_bitset<_Nw>& __x) {
 - for ( size_t __i = 0; __i < _Nw; __i++ ) {
 - _M_w[__i] &= __x._M_w[__i];
 - }
 - }
 - void _M_do_or(const _Base_bitset<_Nw>& __x) {
 - for ( size_t __i = 0; __i < _Nw; __i++ ) {
 - _M_w[__i] |= __x._M_w[__i];
 - }
 - }
 - void _M_do_xor(const _Base_bitset<_Nw>& __x) {
 - for ( size_t __i = 0; __i < _Nw; __i++ ) {
 - _M_w[__i] ^= __x._M_w[__i];
 - }
 - }
 
节选上述代码,可以得到:
1. bitset继承_Base_bitset,具体操作封装在_Base_bitset中
2. bitset 的size作为模板参数(非类型模板参数的一个要求是,编译器能在编译期就能把参数确定下来),因此,bitset大小在编译期固定,不支持插入和删除元素
3. 各种位操作,性能高
4._Base_bitset使unsigned long作为底层存储,不支持指针、引用、迭代器
5. 使用 _WordT _M_w[_Nw];分配内存,因此在栈中定义bitset需要注意大小(和STL标准容器堆内存分配区别开)。
     eg,下面的代码将栈溢出(测试机器栈内存10M)
- void fun()
 - {
 - const int n = 800000000;
 - bitset<n> a;
 - cout << a.size() << endl;
 - }
 - int main(int argc, char** argv)
 - {
 - fun();
 - return 0;
 - }
 
大内存分配可以分配在堆中,如下:
- const int n = 800000000;
 - bitset<n> *a = new(std::nothrow) bitset<n>;
 - if(a)
 - {
 - cout << a->size() << endl;
 - delete a;
 - a = NULL;
 - }
 
4. vector<bool>及deque<bool>
bitset高效,但是size必须在编译器确定,不支持插入和删除。因此,一个可能的替代品是vector<bool>和deque<bool>
两者的区别:
vector<bool>不是一个STL容器,并且不容纳bool(like bitse底层t机制)
deque<bool>是一个STL容器,它保存真正的bool值
分别运行
- deque<bool> a;
 - a[0] = 0;
 - bool* b = &a[0];
 - cout << *b << endl;
 
和
- vector<bool> a;
 - a[0] = 0;
 - bool* b = &a[0];
 - cout << *b << endl;
 
将会发现:
使用deque<bool>正确,而是用vector<bool>会报错:“cannot convert `std::_Bit_reference*' to `bool*' in initialization“
但是,deque简直是在践踏内存。
使用deque<bool>
- int main(int argc, char** argv)
 - {
 - deque<bool> a(10000000000);
 - sleep(100);
 - return 0;
 - }
 
内存使用:
  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                               
23612 work      25   0 9990m 9.8g 
 720 S  0.0 65.0   0:39.35 test
使用vector<bool>
- int main(int argc, char** argv)
 - {
 - vector<bool> a(10000000000);
 - sleep(100);
 - return 0;
 - }
 
内存使用:
  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                               
23909 work      25   0 1198m 1.2g 
 716 S  0.0  7.8   0:01.31 test
使用bitset
- int main(int argc, char** argv)
 - {
 - const unsigned long int n = 10000000000;
 - bitset<n> *a = new(std::nothrow) bitset<n>;
 - sleep(100);
 - return 0;
 - }
 
PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                               
24439 work      25   0 1198m 1.2g 
 712 S 30.7  7.8   0:00.92 test  
10亿个bool,vector<bool>和bitset使用内存1198M,deque<bool>则是9990M
5. 总结
在需要对位集合进行操作的时候,如何操作集合大小比较固定,优先选择高效的bitset;
如果需要动态增删元素,或者编译期间无法确定集合大小,则可以考虑vector<bool>,deque<bool>内存开销太大,基本上不考虑。
参考:
http://www.sgi.com/tech/stl/download.html
http://www.cplusplus.com/reference/stl/vector/
http://www.cplusplus.com/reference/stl/bitset/
扩展阅读:
Vector specialization: vector<bool>
The vector class template has a special template specialization for the bool type.
This specialization is provided to optimize for space allocation: In this template specialization, each element occupies only one bit (which is eight times less than the
 smallest type in C++:char).
The references to elements of a bool vector
 returned by the vector members are not references tobool objects,
 but a special member type which is a reference to a single bit, defined inside thevector<bool> class
 specialization as:
- class vector<bool>::reference {
 - friend class vector;
 - reference(); // no public constructor
 - public:
 - ~reference();
 - operator bool () const; // convert to bool
 - reference& operator= ( const bool x ); // assign from bool
 - reference& operator= ( const reference& x ); // assign from bit
 - void flip(); // flip bit value.
 - }
 
标准非STL容器 : bitset的更多相关文章
- 标准非STL之bitset
		
template <size_t N> class bitset; BitsetA bitset stores bits (elements with only two possible ...
 - STL容器 -- Bitset
		
核心内容:Bitset 是 STL 中的二进制容器, 存放的时 bit 位元素, 每一位只占一个 bit 位, 取值 0 或者 1, 可以像整形元素一样按位与或非, 并且大大优化了时间和空间复杂度. ...
 - STL容器总结
		
一. 种类: 标准STL序列容器:vector.string.deque和list. 标准STL关联容器:set.multiset.map和multimap. 非标准序列容器slist和rope.sl ...
 - STL容器与配接器
		
STL容器包括顺序容器.关联容器.无序关联容器 STL配接器包括容器配接器.函数配接器 顺序容器: vector 行为类似于数组,但可以根据要求 ...
 - STL容器的本质
		
http://blog.sina.com.cn/s/blog_4d3a41f40100eof0.html 最近在学习unordered_map里面的散列函数和相等函数怎么写.学习过程中看到了一个好帖子 ...
 - STL容器底层数据结构的实现
		
C++ STL 的实现: 1.vector 底层数据结构为数组 ,支持快速随机访问 2.list 底层数据结构为双向链表,支持快速增删 3.deque ...
 - [技术] OIer的C++标准库 : STL入门
		
注: 本文主要摘取STL在OI中的常用技巧应用, 所以可能会重点说明容器部分和算法部分, 且不会讨论所有支持的函数/操作并主要讨论 C++11 前支持的特性. 如果需要详细完整的介绍请自行查阅标准文档 ...
 - Sword STL容器分类介绍
		
标准STL序列容器:vector.string.deque和list. 标准STL关联容器:set.multiset.map和multimap. 非标准序列容器slist和rope.slist是一个单 ...
 - STL 容器的概念
		
STL 容器的概念 在实际的开发过程中,数据结构本身的重要性不会逊于操作于数据结构的算法的重要性,当程序中存在着对时间要求很高的部分时,数据结构的选择就显得更加重要. 经典的数据结构数量有限,但是我们 ...
 
随机推荐
- Request failed with status code 500以及自引用循环Self referencing loop detected for property ‘xx‘ with type
			
错误Error: Request failed with status code 500 ,调试前端没问题,后端也没问题,还报错"连接超时" 在Network中找到错误Self r ...
 - Seata的一些概念
			
Seata的一些概念 一.什么是seata 二.AT模式的介绍 1.前提条件 2.整体机制 3.读写隔离的实现 1.写隔离 2.读隔离 三.事务分组 1.事务分组是什么? 2.通过事务分组如何找到后端 ...
 - Noip模拟62 2021.9.26
			
T1 Set 真就随机化拿了$90$?? 不过还是有依据的,毕竟这道题出解的几率很大,随出答案的概率也极大 所以不妨打一个随机化 1 #include<bits/stdc++.h> 2 # ...
 - 回文链表 牛客网 程序员面试金典  C++ Python
			
回文链表 牛客网 程序员面试金典 C++ Python 题目描述 请编写一个函数,检查链表是否为回文. 给定一个链表ListNode* pHead,请返回一个bool,代表链表是否为回文. 测试样例 ...
 - Django(74)drf-spectacular自动生成接口文档
			
介绍 drf-spectacular是为Django REST Framework生成合理灵活的OpenAPI 3.0模式.它可以自动帮我们提取接口中的信息,从而形成接口文档,而且内容十分详细,再也不 ...
 - C++实现一个SOAP客户端
			
目录 简介 实现客户端 准备xml文件 引入库文件 构建请求数据的xml 执行Http协议的POST方法 解析响应数据的xml 测试客户端 附件 简介 在C++中,一般使用gSOAP来实现客户端.服务 ...
 - Kafka 消费迟滞监控工具 Burrow
			
Kafka 官方对于自身的 LAG 监控并没有太好的方法,虽然Kafka broker 自带有 kafka-topic.sh, kafka-consumer-groups.sh, kafka-cons ...
 - MySQL 查询语句(1)
			
一:创建数据库 1:CREATE DATABASE test; //创建数据库test 2:SHOW DATABASES: //查看目前数据库中可用的数据库,默认会有系统数据库 3:USE test; ...
 - loadRunner运行场景时,事务数为0或是只显示添加的事务的数
			
脚本编辑好后,不要着急到controller去执行,注意查看Run-time Settings(运行是设置)-->General(常规)-->Miscellaneous(其他)中查看Aut ...
 - node 中第三方模块的加载过程原理
			
node 中第三方模块的加载过程原理 凡是第三方模块都必须通过 npm 来下载 使用的时候就可以通过require('包名') 的方式来进行加载才可以使用 不可能有任何一个第三方包和核心模块的名字是一 ...