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部分实现源码

[cpp] view
plain
 copy

  1. template<size_t _Nb>
  2. class bitset : private _Base_bitset<__BITSET_WORDS(_Nb)>
  3. {
  4. private:
  5. typedef _Base_bitset<__BITSET_WORDS(_Nb)> _Base;
  6. typedef unsigned long _WordT;
  7. private:
  8. void _M_do_sanitize() {
  9. _Sanitize<_Nb%__BITS_PER_WORD>::_M_do_sanitize(this->_M_hiword());
  10. }
  11. .....
  12. }
[cpp] view
plain
 copy

  1. #define __BITS_PER_WORD (CHAR_BIT*sizeof(unsigned long))
  2. #define __BITSET_WORDS(__n) \
  3. ((__n) < 1 ? 1 : ((__n) + __BITS_PER_WORD - 1)/__BITS_PER_WORD)
[cpp] view
plain
 copy

  1. template<size_t _Nw>
  2. struct _Base_bitset {
  3. typedef unsigned long _WordT;
  4. _WordT _M_w[_Nw];                // 0 is the least significant word.
  5. _Base_bitset( void ) { _M_do_reset(); }
  6. _Base_bitset(unsigned long __val) {
  7. _M_do_reset();
  8. _M_w[0] = __val;
  9. }
  10. static size_t _S_whichword( size_t __pos )
  11. { return __pos / __BITS_PER_WORD; }
  12. static size_t _S_whichbyte( size_t __pos )
  13. { return (__pos % __BITS_PER_WORD) / CHAR_BIT; }
  14. static size_t _S_whichbit( size_t __pos )
  15. { return __pos % __BITS_PER_WORD; }
  16. static _WordT _S_maskbit( size_t __pos )
  17. { return (static_cast<_WordT>(1)) << _S_whichbit(__pos); }
  18. _WordT& _M_getword(size_t __pos)       { return _M_w[_S_whichword(__pos)]; }
  19. _WordT  _M_getword(size_t __pos) const { return _M_w[_S_whichword(__pos)]; }
  20. _WordT& _M_hiword()       { return _M_w[_Nw - 1]; }
  21. _WordT  _M_hiword() const { return _M_w[_Nw - 1]; }
  22. void _M_do_and(const _Base_bitset<_Nw>& __x) {
  23. for ( size_t __i = 0; __i < _Nw; __i++ ) {
  24. _M_w[__i] &= __x._M_w[__i];
  25. }
  26. }
  27. void _M_do_or(const _Base_bitset<_Nw>& __x) {
  28. for ( size_t __i = 0; __i < _Nw; __i++ ) {
  29. _M_w[__i] |= __x._M_w[__i];
  30. }
  31. }
  32. void _M_do_xor(const _Base_bitset<_Nw>& __x) {
  33. for ( size_t __i = 0; __i < _Nw; __i++ ) {
  34. _M_w[__i] ^= __x._M_w[__i];
  35. }
  36. }

节选上述代码,可以得到:

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)

[cpp] view
plain
 copy

  1. void fun()
  2. {
  3. const int n = 800000000;
  4. bitset<n> a;
  5. cout << a.size() << endl;
  6. }
  7. int main(int argc, char** argv)
  8. {
  9. fun();
  10. return 0;
  11. }

大内存分配可以分配在堆中,如下:

[cpp] view
plain
 copy

  1. const int n = 800000000;
  2. bitset<n> *a = new(std::nothrow) bitset<n>;
  3. if(a)
  4. {
  5. cout << a->size() << endl;
  6. delete a;
  7. a = NULL;
  8. }

4. vector<bool>及deque<bool>

bitset高效,但是size必须在编译器确定,不支持插入和删除。因此,一个可能的替代品是vector<bool>和deque<bool>

两者的区别:

vector<bool>不是一个STL容器,并且不容纳bool(like bitse底层t机制)

deque<bool>是一个STL容器,它保存真正的bool值

分别运行

[cpp] view
plain
 copy

  1. deque<bool> a;
  2. a[0] = 0;
  3. bool* b = &a[0];
  4. cout << *b << endl;

[cpp] view
plain
 copy

  1. vector<bool> a;
  2. a[0] = 0;
  3. bool* b = &a[0];
  4. cout << *b << endl;

将会发现:

使用deque<bool>正确,而是用vector<bool>会报错:“cannot convert `std::_Bit_reference*' to `bool*' in initialization“



但是,deque简直是在践踏内存。

使用deque<bool>

[cpp] view
plain
 copy

  1. int main(int argc, char** argv)
  2. {
  3. deque<bool> a(10000000000);
  4. sleep(100);
  5. return 0;
  6. }

内存使用:

  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>

[cpp] view
plain
 copy

  1. int main(int argc, char** argv)
  2. {
  3. vector<bool> a(10000000000);
  4. sleep(100);
  5. return 0;
  6. }

内存使用:

  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

[cpp] view
plain
 copy

  1. int main(int argc, char** argv)
  2. {
  3. const unsigned long int n = 10000000000;
  4. bitset<n> *a = new(std::nothrow) bitset<n>;
  5. sleep(100);
  6. return 0;
  7. }

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:

[cpp] view
plain
 copy

  1. class vector<bool>::reference {
  2. friend class vector;
  3. reference();                                 // no public constructor
  4. public:
  5. ~reference();
  6. operator bool () const;                      // convert to bool
  7. reference& operator= ( const bool x );       // assign from bool
  8. reference& operator= ( const reference& x );  // assign from bit
  9. void flip();                                 // flip bit value.
  10. }

标准非STL容器 : bitset的更多相关文章

  1. 标准非STL之bitset

    template <size_t N> class bitset; BitsetA bitset stores bits (elements with only two possible ...

  2. STL容器 -- Bitset

    核心内容:Bitset 是 STL 中的二进制容器, 存放的时 bit 位元素, 每一位只占一个 bit 位, 取值 0 或者 1, 可以像整形元素一样按位与或非, 并且大大优化了时间和空间复杂度. ...

  3. STL容器总结

    一. 种类: 标准STL序列容器:vector.string.deque和list. 标准STL关联容器:set.multiset.map和multimap. 非标准序列容器slist和rope.sl ...

  4. STL容器与配接器

    STL容器包括顺序容器.关联容器.无序关联容器 STL配接器包括容器配接器.函数配接器 顺序容器: vector                             行为类似于数组,但可以根据要求 ...

  5. STL容器的本质

    http://blog.sina.com.cn/s/blog_4d3a41f40100eof0.html 最近在学习unordered_map里面的散列函数和相等函数怎么写.学习过程中看到了一个好帖子 ...

  6. STL容器底层数据结构的实现

    C++ STL 的实现: 1.vector      底层数据结构为数组 ,支持快速随机访问   2.list            底层数据结构为双向链表,支持快速增删   3.deque     ...

  7. [技术] OIer的C++标准库 : STL入门

    注: 本文主要摘取STL在OI中的常用技巧应用, 所以可能会重点说明容器部分和算法部分, 且不会讨论所有支持的函数/操作并主要讨论 C++11 前支持的特性. 如果需要详细完整的介绍请自行查阅标准文档 ...

  8. Sword STL容器分类介绍

    标准STL序列容器:vector.string.deque和list. 标准STL关联容器:set.multiset.map和multimap. 非标准序列容器slist和rope.slist是一个单 ...

  9. STL 容器的概念

    STL 容器的概念 在实际的开发过程中,数据结构本身的重要性不会逊于操作于数据结构的算法的重要性,当程序中存在着对时间要求很高的部分时,数据结构的选择就显得更加重要. 经典的数据结构数量有限,但是我们 ...

随机推荐

  1. 晶振在电路设计时关于负载电容CL大小取值特别需要注意什么?

    在无源晶体的设计中,经常遇到负载电容CL的大小取值.晶振设计与精度的提高.KHz无源晶振的停止.音叉晶体谐振器的精度漂移以及精度和无源晶振在高温下的精度是否等于低温的精度烦忧的问题等. 无源晶体振荡器 ...

  2. 嵌入式大佬给你分析stm32串口

    stm32作为现在嵌入式物联网单片机行业中经常要用多的技术,相信大家都有所接触,今天这篇就给大家详细的分析下有关于stm32的出口,还不是很清楚的朋友要注意看看了哦,在最后还会为大家分享有些关于stm ...

  3. 加法运算替代 牛客网 程序员面试金典 C++ Python

    加法运算替代 牛客网 程序员面试金典 题目描述 请编写一个方法,实现整数的乘法.减法和除法运算(这里的除指整除).只允许使用加号. 给定两个正整数int a,int b,同时给定一个int type代 ...

  4. CSS学习笔记:display属性

    目录 一.display属性概述 1. 块级元素和行内元素的区别 2.常见的块级元素和行内元素 3. display属性常见的属性值 二.测试display取各属性值的效果 1. 测试inline和b ...

  5. IP数据报中如果不分片,分片标志值是什么?

    过了好久才解决这个简单的问题,罪过罪过- 答案:如果IP数据报不分片,分片标志DF(Don't Fragment)会被设置为1.分片标志MF(More Fragment)设置为0. 下面是详细解释: ...

  6. requestAnimationFrame 执行机制探索

    1.什么是 requestAnimationFrame window.requestAnimationFrame() 告诉浏览器--你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更 ...

  7. 我罗斯方块最终篇(Block类)

    负责的任务 完善Block类的相关函数及变量: 对Block类中函数进行调整改进,并于其他人负责的类相互配合: 对Block类的函数功能进行调试: github项目地址. 效果图 具体可见总篇,一下仅 ...

  8. OOP 4.21晚 指针知识点

    1.读法:int* ptr ptr是一个指针指向整型变量 2.指针类型:指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型; 3.指针所指向的类型:只须把指针声明语句中的指针名字和名字左边的指 ...

  9. OPPO 图数据库平台建设及业务落地

    本文首发于 OPPO 数智技术公众号,WeChat ID: OPPO_tech 1.什么是图数据库 图数据库(Graph database)是以图这种数据结构存储和查询的数据库.与其他数据库不同,关系 ...

  10. 在Vs code中使用sftp插件以及连接windows远程sftp协议部署指导(解决vscode的sftp插件中文目录乱码问题)

    一.启动SFtp 二.上手vs code SFTP插件 2.1 初始配置 2.2解决乱码问题 三.SFTP配置 3.1常用配置 3.2示例配置 四.SFTP使用 五.扩展阅读 一.启动SFtp 话说小 ...