选择可调用对象
Lambda、bind 与指针的比较
选择可调用对象 是 CoddyKit 上的免费 C++ Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C++ Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C++ Academy 课程共包含 4 节课。
可调用对象工具箱
C++ 提供了多种传递行为的方式:函数指针、lambda 表达式、函子、std::bind 和 std::function。每种方式都适用于不同的需求。
- 速度与灵活性之间的权衡最为常见。
- 可读性同样重要。
#include <iostream>
int twice(int x) { return x * 2; }
int main() {
int (*fp)(int) = twice;
auto lam = [](int x) { return x * 2; };
std::cout << fp(5) << ' ' << lam(5) << '\n';
return 0;
}何时使用函数指针
当您需要一个小型、无状态的回调函数,并且希望兼容 C 或实现零额外开销时,可以选择函数指针。
#include <iostream>
void logMsg(const char* m) { std::cout << "LOG: " << m << '\n'; }
int main() {
void (*handler)(const char*) = logMsg;
handler("started");
return 0;
}何时使用 lambda 表达式
lambda 表达式是现代 C++ 的默认选择:写法简洁,可以捕获状态,而且编译器能够在模板和算法中很好地将其内联。
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> v = {3, 1, 4, 1, 5};
std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; });
for (int x : v) std::cout << x << ' ';
std::cout << '\n';
return 0;
}在 lambda 表达式中捕获状态
lambda 表达式可以捕获周围的变量,这是函数指针无法做到的,因此非常适合需要上下文的回调函数。
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
int threshold = 3;
std::vector<int> v = {1, 2, 3, 4, 5};
int c = std::count_if(v.begin(), v.end(), [threshold](int x) { return x > threshold; });
std::cout << c << '\n';
return 0;
}何时使用函子
函子(包含 operator() 的结构体)适合表示可复用的具名操作;它可以保存配置,并在多个位置使用。
#include <iostream>
#include <vector>
#include <algorithm>
struct AboveThreshold {
int limit;
bool operator()(int x) const { return x > limit; }
};
int main() {
std::vector<int> v = {1, 5, 2, 8};
std::cout << std::count_if(v.begin(), v.end(), AboveThreshold{4}) << '\n';
return 0;
}lambda 表达式与 bind
两者都可以预先填充参数。lambda 表达式的写法通常更清晰,也更容易由编译器优化,因此应优先使用它,而不是 std::bind。
#include <iostream>
int power(int base, int exp) {
int r = 1;
for (int i = 0; i < exp; ++i) r *= base;
return r;
}
int main() {
auto square = [](int x) { return power(x, 2); };
std::cout << square(6) << '\n';
return 0;
}何时使用 std::function
当您必须存储具体类型未知的可调用对象时,请使用 std::function,例如将其存储在类成员或容器中。
#include <iostream>
#include <functional>
#include <vector>
int main() {
std::vector<std::function<int(int)>> pipeline;
pipeline.push_back([](int x) { return x + 1; });
pipeline.push_back([](int x) { return x * 3; });
int v = 2;
for (auto& f : pipeline) v = f(v);
std::cout << v << '\n';
return 0;
}模板可避免开销
如果函数只需要接收可调用对象,而不需要存储它,那么模板参数可以避免类型擦除,从而保持完整的执行速度。
#include <iostream>
template <typename F>
int applyTwice(int x, F f) { return f(f(x)); }
int main() {
std::cout << applyTwice(3, [](int n) { return n + 10; }) << '\n';
return 0;
}性能排序
大致来说:使用模板的可调用对象或无捕获的 lambda 表达式最快,函数指针紧随其后,而 std::function 由于间接调用和可能发生的内存分配,速度最慢。
#include <iostream>
#include <functional>
int main() {
auto fast = [](int x) { return x + 1; };
std::function<int(int)> flexible = fast;
std::cout << fast(1) << ' ' << flexible(1) << '\n';
return 0;
}选择指南
快速指南:
- 需要存储任何可调用对象?使用
std::function。 - 只是在通用函数中接收一个可调用对象?使用模板参数。
- 需要带状态的内联行为?使用lambda 表达式。
- 需要 C API 或无状态对象?使用函数指针。
#include <iostream>
#include <functional>
void runStored(const std::function<void()>& cb) { cb(); }
template <typename F>
void runGeneric(F f) { f(); }
int main() {
runStored([] { std::cout << "stored\n"; });
runGeneric([] { std::cout << "generic\n"; });
return 0;
}整合运用
一个小型事件系统可能会以通用方式接收处理函数,但将它们存储在 std::function 中,以便稍后调用。
#include <iostream>
#include <functional>
#include <vector>
struct Dispatcher {
std::vector<std::function<void(int)>> handlers;
template <typename F> void on(F f) { handlers.emplace_back(f); }
void fire(int e) { for (auto& h : handlers) h(e); }
};
int main() {
Dispatcher d;
d.on([](int e) { std::cout << "got " << e << '\n'; });
d.fire(7);
return 0;
}快速检查
测试您对如何选择可调用对象的理解。
回顾
您学会了如何选择可调用对象:
- 无状态且兼容 C 的回调函数使用函数指针
- 内联的有状态行为默认使用lambda 表达式
- 可复用的具名操作使用函子
- 只接收可调用对象时使用模板参数(速度最快)
- 必须存储具体类型未知的可调用对象时使用
std::function
函数对象与 std::bind 课程到此结束。
常见问题解答
「选择可调用对象」课时是免费的吗?
是的 — 「选择可调用对象」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C++ Academy 课程的其余内容,请升级到 CoddyKit PRO。 C++ Academy 课程共包含 4 节课。
「选择可调用对象」这节课中我会学到什么?
Lambda、bind 与指针的比较 你通过在浏览器中直接运行的动手代码来练习 C++ Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 C++ Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 C++ Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「选择可调用对象」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 C++ Academy 课中编写并运行代码吗?
能。每节 C++ Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 函数指针
- std::function
- std::bind
- 选择可调用对象