第25章 使用STL位标志
位是存储设置与标志的高效方法。标准模板库(STL)提供了可帮助组织与操作位信息的类。
在本章中,您将学习:
• bitset类;
• vector<bool>。
25.1 bitset类
std::bitset是一个STL类,用于处理以位和位标志表示的信息。std::bitset不是STL容器类,因为它不能调整长度。这是一个实用类,针对处理长度在编译阶段已知的位序列进行了优化。
要使用std::bitset类,必须包含头文件<bitset>:
实例化std::bitset
实例化这个模板类时,必须通过一个模板参数指定实例需要管理的位数:
还可将bitset初始化为一个用字符串字面量(char*)表示的位序列:
使用一个bitset来实例化另一个bitset非常简单:
程序清单25.1演示了一些实例化bitset类的方式。
程序清单25.1 实例化std::bitset
输出:
分析:
该示例演示了4种创建bitset对象的方式:通过默认构造函数将位序列初始化为0(如第9行所示);通过C风格字符串指定位序列的字符串表示(如第 12行所示);通过 unsigned long指定二进制序列对应的十进制值(如第 15行所示);使用复制构造函数(如第 19行所示)。注意,在每个实例中,都需要通过一个模板参数指定位序列包含的位数。位数在编译阶段指定,而不是动态的。指定bitset的位数后,便不能插入更多的位,而不像vector那样调整在编译阶段指定的长度。
25.2 使用std::bitset及其成员
bitset类提供了很多成员函数,可用于在bitset中插入位、设置(重置)内容、读取内容(将内容写入到流中)。它还提供了一些运算符,用于显示位序列、执行按位逻辑运算等。
第12章介绍了运算符,还介绍了运算符最重要的作用是提高类的可用性。std::bitset提供一些很有用的运算符,如表25.1所示,这些运算符让bitset使用起来非常容易。表25.1通过程序清单25.1所示的bitset对象fourBits演示这些运算符的用法。
表25.1 std::bitset提供的运算符
续表
除这些运算符外,std::bitset还提供了|=、&=、^=和~=等运算符,用于对bitset对象执行按位操作。
位可以存储两种状态:要么是已设置(1),要么是重置(0)。要对bitset的内容进行操作,可使用表25.2列出的成员函数对bitset中的一位或所有位进行操作。
表25.2 std::bitset
程序清单25.2演示了这些成员方法和运算符的用法。
程序清单25.2 使用bitset执行逻辑运算
输出:
分析:
该示例是一个交互式程序,它不仅演示了使用std::bitset在两个位序列之间执行按位运算很简单,还演示了如何使用std::bitset的流运算符。std::bitset实现了移位运算符(>>和<<),让您能够轻松地将位序列打印到屏幕上以及读取用户以字符串形式输入的位序列。第 10 行将用户提供的序列填充到inputBits中。第12行使用count()获得序列中值为1的位数。为计算序列中值为零的位数,将返回bitset中位数的size()与count()相减,如第14行所示。inputFlipped位于inputBits的一个副本的开头,然后使用flip()将序列中所有位取反,如第17行所示。现在它包含的序列中每位的值都与原来相反(即0变成1,1变成0)。其他代码演示了对两个bitset执行AND、OR和XOR等操作的结果。
STL bitset的缺点之一是不能动态地调整长度。仅当在编辑阶段知道序列将存储多少位时才能使用bitset。
为克服这种缺点,STL向程序员提供了vector<bool>类(在有些STL实现中为bit_vector)。
25.3 vector<bool>
vector<bool>是对std::vector的部分具体化,用于存储布尔数据。这个类可动态地调整长度,因此程序员无需在编译阶段知道要存储的布尔标志数。
要使用std::vector<bool>类,必须包含头文件<vector>:
实例化vector<bool>的方式与实例化vector类似,有一些方便的重载构造函数可供使用:
例如,可创建一个这样的 vector,即它最初包含 10 个布尔元素,且每个元素都被初始化为 1 (即true):
还可使用一个vector<bool>创建另一个vector<bool>:
程序清单25.3演示了一些实例化vector<bool>的方式。
程序清单25.3 实例化vector<bool>
分析:
该示例演示了一些创建vector<bool>对象的方式:第7行使用默认构造函数;第10行创建了一个包含10个布尔标志的对象,其中每个标志的值都为true;第13行演示了如何通过复制vector<bool>对象来创建另一个vector<bool>对象。
vector<bool>提供了函数flip(),用于将序列中的布尔值取反,这与函数bitset<>::flip()很像。
除了这个方法外,vector<bool>与std::vector极其相似,例如,可使用push_back将标志位插入到序列中。程序清单25.4更详细地演示了这个类的用法。
程序清单25.4 使用vector<bool>
输出:
分析:
在这个示例中,通过运算符[]访问了 vector 中的布尔标志(如第 7~9 行所示),这与访问普通vector很像。第18行使用函数flip()将位标志取反,即将所有0都转换为1,将所有1都转换为0。注意到第11行使用了push_back。虽然第6行将vecBoolFlags初始化为包含3个标志,但可动态地添加标志,如第11行所示;而使用std::bitset时,标志数是在编译阶段指定的,不能增加。
25.4 总结
本章介绍了用于处理位序列和位标志最有效的工具:std::bitset 类。另外还介绍 vector<bool>类,它也可以存储布尔标志——其位数不需要在编译时就确定。
25.5 问与答
问:在可以使用std::bitset和vector<bool>的情况下,您将选择使用哪个类来存储二进制标志?
答:bitset,因为它更适合这种需求。
问:假设有一个名为myBitSeq的std::bitset对象,它包含一定数量的位。如何确定它包含多少个值为0(或false)的位?
答:bitset::count()返回值为1的位数,bitset::size()返回总位数,将后者减去前者将得到序列中值为0的位数。
问:能否使用迭代器来访问vector<bool>中的元素?
答:可以。vector<bool>是std::vector的部分具体化,它支持迭代器。
问:能否在编译阶段指定要存储在vector<bool>中的元素个数?
答:可以。为此,可调用重载构造函数并指定元素数,也可在实例化后调用函数vector<bool>::resize。
25.6 作业
作业包括测验和练习,前者帮助读者加深对所学知识的理解,后者提供了使用新学知识的机会。请尽量先完成测验和练习题,然后再对照附录 D 的答案。在继续学习下一章前,请务必弄懂这些答案。
1.bitset能否扩展其内部缓冲区以存储可变的元素数?
2.为什么bitset不属于STL容器类?
3.您会使用std::vector来存储位数在编译阶段就知道的固定位数吗?
1.创建一个长4位的bitset对象,并使用一个数字来初始化它,然后显示结果并将其与另一个bitset对象相加(注意:bitsets不支持语法 bitsetA = bitsetX + bitsetY)。
2.请演示如何将bitset对象中的位取反。