0Pricing
C++ Academy · 课时

选择可调用对象

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 反馈 — 无需本地设置。

此课程中的所有课时

  1. 函数指针
  2. std::function
  3. std::bind
  4. 选择可调用对象
← 返回 C++ Academy