C++ 标准库 :让函数像变量一样自由流动
在 C++ 的世界里,函数是程序的“行为单元”。但传统的函数调用方式,往往让代码变得僵硬——我们只能在编译时确定调用哪个函数。而 C++ 标准库中的
这不仅仅是语法糖,而是一次编程范式的跃迁。通过
本文将带你从零开始,逐步掌握
函数对象与函数指针的对比
在学习
#include <iostream>
// 定义一个简单的加法函数
int add(int a, int b) {
return a + b;
}
// 定义一个减法函数
int subtract(int a, int b) {
return a - b;
}
// 使用函数指针作为参数
void operate(int x, int y, int (*func)(int, int)) {
std::cout << "结果: " << func(x, y) << std::endl;
}
int main() {
operate(10, 5, add); // 输出: 结果: 15
operate(10, 5, subtract); // 输出: 结果: 5
return 0;
}
这段代码虽然能工作,但有几个问题:函数指针语法复杂,可读性差;无法保存函数状态;不能处理 lambda 表达式。
而 std::function 就是为了解决这些问题而生。它是一个通用的函数包装器,可以容纳函数、函数指针、lambda、仿函数(函数对象)等各种可调用对象。
std::function:统一的函数接口
std::function 是
下面是一个使用 std::function 的示例:
#include <iostream>
#include <functional>
// 定义一个加法函数
int add(int a, int b) {
return a + b;
}
// 定义一个乘法函数
int multiply(int a, int b) {
return a * b;
}
// 使用 std::function 包装函数
void execute_operation(int x, int y, std::function<int(int, int)> op) {
std::cout << "执行操作: " << op(x, y) << std::endl;
}
int main() {
// 使用函数指针
execute_operation(4, 5, add);
// 使用 lambda 表达式
execute_operation(4, 5, [](int a, int b) {
return a * a + b * b; // 平方和
});
// 使用 std::function 存储
std::function<int(int, int)> operation = multiply;
std::cout << "存储的乘法结果: " << operation(3, 4) << std::endl;
return 0;
}
关键点说明:
std::function<int(int, int)>表示这个对象可以接受两个 int 参数,返回一个 int。- 它能接收函数、lambda、仿函数等,统一处理。
- 你可以把函数对象保存在变量中,后续随时调用,灵活性极高。
std::bind:函数参数的“预设器”
在实际开发中,我们常常需要固定某些参数,只改变其他参数。比如,你有一个函数 divide(int a, int b),但你只想固定除数为 2,然后创建一个“除以 2”的函数。
这就是 std::bind 的用武之地。它就像一个“参数填空机”,帮你把函数的部分参数“提前填好”,生成一个新函数。
#include <iostream>
#include <functional>
// 除法函数
double divide(double a, double b) {
if (b == 0) {
std::cerr << "错误:除数不能为 0" << std::endl;
return 0;
}
return a / b;
}
int main() {
// 使用 std::bind 固定除数为 2
auto divide_by_2 = std::bind(divide, std::placeholders::_1, 2.0);
// 现在可以只传一个参数
std::cout << "10 / 2 = " << divide_by_2(10.0) << std::endl; // 输出: 5
std::cout << "15 / 2 = " << divide_by_2(15.0) << std::endl; // 输出: 7.5
// 也可以固定第一个参数
auto multiply_by_3 = std::bind(multiply, 3, std::placeholders::_1);
std::cout << "3 * 4 = " << multiply_by_3(4) << std::endl; // 输出: 12
return 0;
}
这里的关键是 std::placeholders::_1,它表示“第一个参数位置”,你可以用 _2, _3 等表示其他参数位置。std::bind 让你能够轻松创建“部分应用函数”,是函数式编程的重要基础。
| 占位符 | 含义 |
|---|---|
_1 |
第一个参数 |
_2 |
第二个参数 |
_3 |
第三个参数 |
注意:std::placeholders 是命名空间,必须包含 <functional>。
仿函数与 lambda:函数对象的两种形态
在 std::function 包装。
仿函数是一个重载了 () 操作符的类。它比普通函数更灵活,因为它可以有状态。
#include <iostream>
#include <functional>
// 仿函数:计数器
class Counter {
private:
int count;
public:
Counter() : count(0) {}
// 重载 () 操作符,使对象可调用
int operator()(int x) {
count++;
std::cout << "调用第 " << count << " 次,参数: " << x << std::endl;
return x * 2;
}
};
int main() {
Counter c;
std::function<int(int)> func = c;
func(5); // 输出: 调用第 1 次,参数: 5
func(10); // 输出: 调用第 2 次,参数: 10
return 0;
}
lambda 表达式则是现代 C++ 中最简洁的函数对象写法。它允许你在代码中“内联”定义函数。
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
// 使用 lambda 过滤大于 2 的数
std::vector<int> filtered;
std::copy_if(numbers.begin(), numbers.end(), std::back_inserter(filtered),
[](int x) { return x > 2; });
// 使用 lambda 排序(从大到小)
std::sort(numbers.begin(), numbers.end(),
[](int a, int b) { return a > b; });
std::cout << "排序后: ";
for (int n : numbers) {
std::cout << n << " ";
}
std::cout << std::endl;
return 0;
}
lambda 表达式在 std::sort、std::transform 等算法中极为常见,是现代 C++ 编程的标配。
实战案例:策略模式的实现
让我们用
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
// 排序策略接口
void sort_with_strategy(std::vector<int>& data, std::function<bool(int, int)> comparator) {
std::sort(data.begin(), data.end(), comparator);
}
int main() {
std::vector<int> numbers = {5, -2, 8, -1, 3};
// 1. 升序
sort_with_strategy(numbers, [](int a, int b) { return a < b; });
std::cout << "升序: ";
for (int n : numbers) std::cout << n << " ";
std::cout << std::endl;
// 2. 降序
sort_with_strategy(numbers, [](int a, int b) { return a > b; });
std::cout << "降序: ";
for (int n : numbers) std::cout << n << " ";
std::cout << std::endl;
// 3. 按绝对值排序
sort_with_strategy(numbers, [](int a, int b) {
return std::abs(a) < std::abs(b);
});
std::cout << "按绝对值: ";
for (int n : numbers) std::cout << n << " ";
std::cout << std::endl;
return 0;
}
这个例子展示了 if-else 或 switch 来选择排序方式,而是把策略作为参数传递,代码更清晰、扩展性更强。
总结与进阶建议
C++ 标准库 std::function、std::bind、lambda 表达式和仿函数,你获得了函数式编程的思维能力,让代码更具表达力和可维护性。
对于初学者,建议从 std::function 和 lambda 开始,熟悉其语法;中级开发者可深入研究 std::bind 的参数绑定机制,以及如何在 STL 算法中灵活使用。高级用户则可以探索 std::reference_wrapper、std::mem_fn 等更复杂的用法。
记住:函数不仅是执行指令的工具,更是可以被组合、传递、存储的“一等公民”。掌握