bitset是C++标准库的一种处理二进制位集的模板类,简化了位集的处理,使用指定的模版参数类型size_t,在声明bitset类型时只能使用字面量常量或者const变量作为模版参数。使用时必须包含bitset头文件 ,其模板类声明定义如下,私有继承自内部实现的基类。
template<size_t _Nb> class bitset : private _Base_bitset<_Nb>
{
...
}
在定义bitset时,要明确bitset是多少位,须在尖括号中制定它的长度值。给定的长度必须是常量表达式,也即必须定义为整型字面值常量或const对象。
bitset<32> bitvec;
unsigned long值将转换为二进制的位模式,若bitset类型长度大于unsigned long的二进制位数,则其余高阶位置0;若小于,则使用unsigned long的低阶位,超过bitset的高阶位被丢弃。
注:低阶位-高阶位
计算机的内存存储方式由CPU决定,不过为了兼容性,现在AMD和INTEL都是用相同的方式存放:低字节放在前面,高字节放在后面。 要明白,后面的内存地址比前面大1,也就是低字节放在地址小的空间,高字节放在地址大的空间。
综上所述:无论对于bitset还是计算机机器字的内存存储方式(其实是同一个),只要记住低阶对应于内存地址较小小的空间,高阶对应于内存地址较大的空间。(低対低,高对高,一一对应)。
知道了以上这些,对于bitset的存储方式,就很容易理解了。例:
bitset<16> bitvec1(0xfffa);
bitset<32> bitvec2(0xffff);
cout<<"bitvec1:"<<bitvec1<<endl;
cout<<"bitvec2:"<<bitvec2<<endl;
输出:
bitvec1:1111111111111010
bitvec2:00000000000000001111111111111010
这时奇怪的事发生了,输出并不是期待的顺序,而是相反的。这里还要说的是,bitset实现的<<操作符输出时,将会从bitset的高阶–>低阶输出(更自然)。因此若用for循环从0开始输出就会是实际存储的顺序了。
cout<<"bitvec1(RAW):"<<bitvec1<<endl;
cout<<"bitvec1(FOR):";
for(size_t bs1 = 0;bs1!=bitvec1.size();bs1++)
{
cout<<bitvec1[bs1];
}
cout<<endl;
cout<<"bitvec2(RAW):"<<bitvec2<<endl;
cout<<"bitvec2(FOR):";
for(size_t bs1 = 0;bs1!=bitvec2.size();bs1++)
{
cout<<bitvec2[bs1];
}
cout<<endl;
输出为:
bitvec1(RAW):1111111111111010
bitvec1(FOR):0101111111111111
bitvec2(RAW):00000000000000001111111111111010
bitvec2(FOR):01011111111111110000000000000000
当用string对象初始化bitset对象时,string对象直接表示为位模式。从string对象读入位集的顺序是从右到左。
string strval("1100");
bitset<8> bitvec4(strval);
cout<<"bitvec4(RAW):"<<bitvec4<<endl;
cout<<"bitvec4(FOR):";
for(size_t bs = 0;bs!=bitvec4.size();bs++)
{
cout<<bitvec4[bs];
}
输出:
bitvec4(RAW):00001100
bitvec4(FOR):00110000
上述string对象strval可看成字符数组{‘1’, ‘1’, ‘0’, ‘0’}.
从上可以看出,string对象与bitset对象之间是反向转化的:string对象的最右边字符(既下标最大的那个字符)用来初始化bitset对象的低阶位(即下标为0的位)。这是与用unsigned值初始化bitset对象不同的。即不是遵从低阶高阶一一对应的。
截取子串作为初始值。
string str("1111111000000011001101");
bitset<32> bitvec5(str,5,4);
bitset<32> bitvec6(str,str.size()-4);
cout<<"bitvec5:"<<bitvec5<<endl;
cout<<"bitvec6:"<<bitvec6<<endl;
输出:
bitvec5:00000000000000000000000000001100
bitvec6:00000000000000000000000000001101
bitvec5用str[5]开始的4个字符子串来初始化。
如果省略第3个参数,则取从开始位置一直到string对象末尾的所有字符,bitvec6用str末尾的4位来初始化。
bool is_set = bitvec6.any(); //是否存在置为1 的位
bool is_not_set = bitvec6.none(); //是否不存在置为1的数
size_t bits_set = bitvec6.count(); //置为1的数
cout<<"bits_set: "<<bits_set<<endl;
size_t sz = bitvec6.size(); //返回bitset对象中对应的二进制位的个数
cout<<"bits_size: "<<sz<<endl;
可以用下标操作来读写某个索引的二进制位。
//访问bitset对象中的位
for(int index = 0;index != bitvec6.size(); index++)
{
bitvec6[index] = 1;
}
//还可调用set,reset等操作
for(int index = 0;index != bitvec6.size();index++)
{
bitvec6.set(index);
}
//测试
if(bitvec6.test(3))
{
}
//等价于
if(bitvec6[3])
{
}
bitvec5.set(); //设置所有
bitvec5.reset(); //重置所有
//取反
bitvec5.flip(2); //对某位取反
bitvec5[2].flip();
bitvec5.flip(); //对所有位取反
to_long 操作返回一个unsigned long 值,该值与bitset的位模式存储值相同。仅当bitset 类型的长度小于或等于unsigned long 的长度,才可用to_long操作:
bitset<132> bitvec7(124);
cout<<"bitvec7: "<<bitvec7<<endl;
unsigned long ulong = bitvec7.to_ullong();
cout<<ulong<<endl;
如果bitset对象包含的二进制位数超过 unsigned long 的长度,将会产生运行时异常。
bitset 也支持内置的位操作符,这些操作符只适用于整型操作数。
cout<<"bitvec7: "<<(bitvec7&=0xf)<<endl;
bitset以8个字节为配置单位,根据声明时传入的表示位数长度的常量表达式进行。例如,1到64均占据八个字节,超过64小于128时占用16个字节,每次超过范围就增加8个字节的长度。实例如下:
bitset<40> bs1 = 1;
cout << sizeof(bs1) << endll;
bitset<100> bs2 = 2;
cout << sizeof(bs2) << endl;
输出:
8
16
因此,如果是以节省内存未首要考虑点的场景,对于较少位数的bitset并没有实际的那么节省,只有对于比较多的位数的场景才能体现出来优势。