C++ 位函数对象
什么是位函数对象
在 C++ STL 中,位函数对象(bit function objects)是一组特殊的函数对象,专门用于处理整数类型的按位逻辑运算。这些函数对象封装了常见的位操作符(如 &、|、^、~ 等),使得我们可以在算法中灵活地执行位运算。
位函数对象定义在 <functional>
头文件中,是 STL 函数对象家族的一部分。与其他函数对象一样,它们也可以与 STL 算法配合使用,增强代码的可读性和通用性。
常用的位函数对象
C++ STL 提供了以下几种常用的位函数对象:
bit_and<T>
:执行按位与(&)操作bit_or<T>
:执行按位或(|)操作bit_xor<T>
:执行按位异或(^)操作bit_not<T>
:执行按位取反(~)操作
其中,T
是模板参数,表示要操作的整数类型,如 int
、long
、char
等。
位函数对象的基本用法
让我们通过一些简单的例子来了解位函数对象的基本用法:
#include <iostream>
#include <functional>
int main() {
// 创建位函数对象
std::bit_and<int> bitAnd;
std::bit_or<int> bitOr;
std::bit_xor<int> bitXor;
std::bit_not<int> bitNot;
int a = 5; // 二进制: 0101
int b = 3; // 二进制: 0011
// 使用位函数对象
std::cout << "a & b = " << bitAnd(a, b) << std::endl; // 输出: 1 (0001)
std::cout << "a | b = " << bitOr(a, b) << std::endl; // 输出: 7 (0111)
std::cout << "a ^ b = " << bitXor(a, b) << std::endl; // 输出: 6 (0110)
std::cout << "~a = " << bitNot(a) << std::endl; // 输出: -6 (取决于整数表示方式)
return 0;
}
输出:
a & b = 1
a | b = 7
a ^ b = 6
~a = -6
位函数对象与 STL 算法结合
位函数对象的真正威力在于与 STL 算法的结合使用。下面是一些常见的应用场景:
使用 transform 算法进行批量位运算
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> v1 = {1, 2, 3, 4, 5};
std::vector<int> v2 = {5, 4, 3, 2, 1};
std::vector<int> result(5);
// 对两个向量中的对应元素执行按位异或操作
std::transform(v1.begin(), v1.end(), v2.begin(),
result.begin(), std::bit_xor<int>());
std::cout << "异或结果: ";
for (int n : result) {
std::cout << n << " ";
}
std::cout << std::endl;
return 0;
}
输出:
异或结果: 4 6 0 6 4
使用 accumulate 算法计算多个数的按位操作结果
#include <iostream>
#include <functional>
#include <vector>
#include <numeric>
int main() {
std::vector<int> numbers = {0b1010, 0b1100, 0b0110, 0b0011};
// 计算所有数字按位与的结果
int andResult = std::accumulate(numbers.begin(), numbers.end(),
~0, std::bit_and<int>());
// 计算所有数字按位或的结果
int orResult = std::accumulate(numbers.begin(), numbers.end(),
0, std::bit_or<int>());
std::cout << "按位与结果: " << andResult << " (二进制: "
<< std::bitset<4>(andResult) << ")" << std::endl;
std::cout << "按位或结果: " << orResult << " (二进制: "
<< std::bitset<4>(orResult) << ")" << std::endl;
return 0;
}
上面的代码中使用了 std::bitset
来显示二进制表示,需要包含 <bitset>
头文件。
实际应用案例
案例1:使用位函数对象实现权限管理系统
在软件开发中,权限管理系统通常使用位运算来表示不同的权限组合。下面是一个使用位函数对象实现的简单权限管理系统:
#include <iostream>
#include <functional>
#include <vector>
#include <string>
// 定义权限
enum Permission {
NONE = 0, // 0000
READ = 1, // 0001
WRITE = 2, // 0010
EXECUTE = 4, // 0100
ADMIN = 8 // 1000
};
// 用户类
class User {
private:
std::string name;
int permissions;
public:
User(const std::string& name, int permissions) : name(name), permissions(permissions) {}
std::string getName() const { return name; }
int getPermissions() const { return permissions; }
// 添加权限
void addPermission(Permission perm) {
std::bit_or<int> bitOr;
permissions = bitOr(permissions, perm);
}
// 移除权限
void removePermission(Permission perm) {
std::bit_and<int> bitAnd;
permissions = bitAnd(permissions, ~perm);
}
// 检查是否有某权限
bool hasPermission(Permission perm) const {
std::bit_and<int> bitAnd;
return bitAnd(permissions, perm) == perm;
}
};
int main() {
User user("Alice", READ | WRITE); // 使用位或组合权限
std::cout << user.getName() << " 的初始权限: ";
std::cout << (user.hasPermission(READ) ? "读 " : "")
<< (user.hasPermission(WRITE) ? "写 " : "")
<< (user.hasPermission(EXECUTE) ? "执行 " : "")
<< (user.hasPermission(ADMIN) ? "管理员" : "")
<< std::endl;
// 添加执行权限
user.addPermission(EXECUTE);
std::cout << "添加执行权限后: ";
std::cout << (user.hasPermission(READ) ? "读 " : "")
<< (user.hasPermission(WRITE) ? "写 " : "")
<< (user.hasPermission(EXECUTE) ? "执行 " : "")
<< (user.hasPermission(ADMIN) ? "管理员" : "")
<< std::endl;
// 移除写权限
user.removePermission(WRITE);
std::cout << "移除写权限后: ";
std::cout << (user.hasPermission(READ) ? "读 " : "")
<< (user.hasPermission(WRITE) ? "写 " : "")
<< (user.hasPermission(EXECUTE) ? "执行 " : "")
<< (user.hasPermission(ADMIN) ? "管理员" : "")
<< std::endl;
return 0;
}
输出:
Alice 的初始权限: 读 写
添加执行权限后: 读 写 执行
移除写权限后: 读 执行
案例2:使用位函数对象处理位图
位图(Bitmap)是一种使用位表示状态的数据结构,通过位函数对象,我们可以方便地对位图进行操作:
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
class Bitmap {
private:
std::vector<unsigned char> data;
size_t size;
public:
Bitmap(size_t size) : size(size) {
// 每8位存储在1个字节中
data.resize((size + 7) / 8, 0);
}
// 设置指定位为1
void set(size_t pos) {
if (pos >= size) return;
std::bit_or<unsigned char> bitOr;
data[pos / 8] = bitOr(data[pos / 8], (1 << (pos % 8)));
}
// 清除指定位(设为0)
void clear(size_t pos) {
if (pos >= size) return;
std::bit_and<unsigned char> bitAnd;
data[pos / 8] = bitAnd(data[pos / 8], ~(1 << (pos % 8)));
}
// 检查指定位是否为1
bool test(size_t pos) const {
if (pos >= size) return false;
std::bit_and<unsigned char> bitAnd;
return bitAnd(data[pos / 8], (1 << (pos % 8))) != 0;
}
// 翻转指定位
void flip(size_t pos) {
if (pos >= size) return;
std::bit_xor<unsigned char> bitXor;
data[pos / 8] = bitXor(data[pos / 8], (1 << (pos % 8)));
}
// 获取位图大小
size_t getSize() const {
return size;
}
};
int main() {
Bitmap bitmap(16); // 创建一个16位的位图
// 设置一些位
bitmap.set(1);
bitmap.set(4);
bitmap.set(7);
bitmap.set(10);
// 显示位图
std::cout << "初始位图: ";
for (size_t i = 0; i < bitmap.getSize(); ++i) {
std::cout << (bitmap.test(i) ? "1" : "0");
}
std::cout << std::endl;
// 翻转一些位
bitmap.flip(1);
bitmap.flip(5);
std::cout << "翻转后位图: ";
for (size_t i = 0; i < bitmap.getSize(); ++i) {
std::cout << (bitmap.test(i) ? "1" : "0");
}
std::cout << std::endl;
return 0;
}
输出:
初始位图: 0100100010000000
翻转后位图: 0000100110000000
C++ 17 透明操作符函数对象
从 C++17 开始,STL 引入了透明操作符函数对象(transparent operator function objects),它们不需要指定模板参数类型。这些函数对象定义在命名空间 std::ranges
中:
#include <iostream>
#include <functional>
int main() {
// C++17 透明操作符函数对象
std::cout << "使用透明操作符函数对象:" << std::endl;
// 不需要指定模板参数类型
std::ranges::bit_and bit_and_op;
std::ranges::bit_or bit_or_op;
std::ranges::bit_xor bit_xor_op;
std::ranges::bit_not bit_not_op;
int a = 5; // 二进制: 0101
int b = 3; // 二进制: 0011
std::cout << "a & b = " << bit_and_op(a, b) << std::endl;
std::cout << "a | b = " << bit_or_op(a, b) << std::endl;
std::cout << "a ^ b = " << bit_xor_op(a, b) << std::endl;
std::cout << "~a = " << bit_not_op(a) << std::endl;
return 0;
}
透明操作符函数对象需要 C++17 或更高版本支持,并且在某些编译器中可能需要包含额外的头文件。
总结
C++ STL 中的位函数对象提供了一种灵活、通用的方式来执行位运算操作。它们的主要优势包括:
- 与 STL 算法兼容:可以直接用于
transform
、accumulate
等算法中 - 增强代码可读性:比直接使用位运算符更清晰地表达意图
- 提高代码可重用性:可以轻松地在不同场景中复用位运算逻辑
位函数对象在权限管理、位图操作、网络编程中的位掩码操作等场景中都有广泛应用。理解并掌握这些函数对象可以帮助我们编写更加简洁、高效的位运算代码。
练习
- 实现一个简单的二进制计算器,支持两个二进制数的按位与、或、异或和非操作。
- 使用位函数对象实现一个简单的集合类,支持并集(union)、交集(intersection)和差集(difference)操作。
- 扩展本文中的位图类,添加一个
count()
方法来统计值为 1 的位的数量。