C++ std::bitset
std::bitset
- 1. 基本概念
- 2. 常用成员函数
- 构造函数
- set()
- reset()
- flip()
- test()
- operator[] 重载
- count()
- any()、none() 和 all()
- to_string() 和 to_ulong() / to_ullong()
- 3. 位运算与操作符重载
- 4. 使用示例
- 5. 应用场景
- 6. 注意事项
std::bitset 是 C++ 标准库中定义在头文件 中的一个类模板,用于表示固定大小的二进制位序列。它为位操作提供了一个简单而高效的接口,常用于需要处理大量布尔值、标志位、位运算和位掩码等场景。
1. 基本概念
-
固定大小
std::bitset 模板的参数 N 表示二进制位的个数,这个大小在编译时就已经确定。与 std::vector 相比,std::bitset 不支持动态扩展,其大小是固定不变的。 -
内存存储与效率
内部以紧凑的形式存储数据,通常采用一个或多个整数类型来存储所有位,能够在空间和时间上提供高效的位运算。
2. 常用成员函数
构造函数
- std::bitset b;
默认构造,所有位初始化为 0。 - std::bitset b(val);
其中 val 可以是整数或字符串,用于初始化位序列。例如,通过字符串 “1011” 初始化。
set()
用于将某个位设置为 1。
- b.set(pos); 将位置 pos 的位设为 1。
- b.set(); 将所有位全部设置为 1。
- b.set(pos, value); 根据布尔值 value 设置对应位。
reset()
将某个位或所有位复位为 0。
- b.reset(pos); 将位置 pos 的位设为 0。
- b.reset(); 将所有位全部复位为 0。
flip()
用于将某个位或所有位进行反转(0 变 1,1 变 0)。
- b.flip(pos); 反转位置 pos 的位。
- b.flip(); 反转所有位。
test()
检查某个位的值,返回一个布尔值。
- b.test(pos); 如果位置 pos 为 1,则返回 true,否则返回 false。
operator[] 重载
- 提供对单个位的读写访问,可以像数组一样使用 b[pos] 来访问特定位,但返回的是一个 proxy 对象,不是直接的 bool 值。
count()
返回二进制序列中 1 的个数。
any()、none() 和 all()
- any():如果至少有一位为 1,则返回 true。
- none():如果所有位均为 0,则返回 true。
- all():如果所有位均为 1,则返回 true。
to_string() 和 to_ulong() / to_ullong()
- to_string():将 std::bitset 转换为 std::string,其中每个字符代表一位。
- to_ulong() 与 to_ullong():将位序列转换为无符号长整型数,但前提是位数必须不超过目标数据类型的位宽,否则会抛出异常。
3. 位运算与操作符重载
std::bitset 重载了常见的位运算符,使得对位操作更加直观:
-
按位与 (&)、按位或 (|) 与按位异或 (^)
可以直接对两个 std::bitset 对象进行与、或、异或运算,返回新的 std::bitset。 -
按位取反 (~)
可以对一个 std::bitset 对象进行取反操作。 -
移位操作 (<< 和 >>)
支持左移和右移操作,操作结果为新的 std::bitset。 -
比较操作符
支持相等 (==) 和不等 (!=) 的比较。
这些操作符使得在处理位级别数据时,代码更为简洁和直观。
4. 使用示例
下面是一个简单示例,展示了 std::bitset 的常用操作:
#include <iostream>
#include <bitset>
#include <string>
int main() {
// 创建一个大小为 8 的 bitset,初始值为 0
std::bitset<8> bs;
std::cout << "初始 bs: " << bs << std::endl; // 输出: 00000000
// 设置第 2 位为 1
bs.set(2);
std::cout << "设置第 2 位后: " << bs << std::endl; // 输出: 00000100
// 将所有位设为 1
bs.set();
std::cout << "全部置 1 后: " << bs << std::endl; // 输出: 11111111
// 复位第 4 位
bs.reset(4);
std::cout << "复位第 4 位后: " << bs << std::endl; // 输出: 11101111
// 反转所有位
bs.flip();
std::cout << "反转后: " << bs << std::endl; // 输出: 00010000
// 判断某个位的状态
bool isSet = bs.test(4);
std::cout << "第 4 位状态: " << isSet << std::endl; // 输出: true
// 统计 1 的个数
std::cout << "1 的个数: " << bs.count() << std::endl; // 输出: 1
// 将 bitset 转换为字符串
std::string bitStr = bs.to_string();
std::cout << "字符串表示: " << bitStr << std::endl; // 输出: 00010000
return 0;
}
5. 应用场景
- 标志位管理
当需要同时管理多个布尔标志(例如状态位、权限标记、配置开关)时,std::bitset 可以用来高效地存储和操作这些标志。 - 位掩码与位操作
在需要进行位级操作(例如网络协议、硬件接口、压缩算法)时,std::bitset 能够提供清晰和简洁的接口。 - 性能优化
由于内存占用紧凑和支持直接的位操作,std::bitset 在某些场景下可以提供比动态容器更高的效率。
6. 注意事项
-
固定大小限制
由于大小在编译时确定,不能根据运行时数据动态调整。如果需要动态大小的位容器,可以考虑 std::vector 或其他专门的位向量库。 -
溢出与转换问题
使用 to_ulong() 或 to_ullong() 时要确保当前 bitset 的大小不超过目标类型的位宽,否则可能会抛出 std::overflow_error 异常。 -
操作符[] 返回的是 proxy 对象
使用 operator[] 修改单个位时,其返回的是一个代理对象,而不是直接的布尔引用,这可能会导致一些意想不到的行为,需要注意这一点。