目录一、Lambda 的基本语法二、捕获列表Capture值捕获 []引用捕获 []混合捕获特定变量捕获C14 广义捕获带初始值C17 捕获 *this三、mutable 关键字四、泛型 LambdaC14五、std::function统一的可调用对象包装器存储成员函数六、Lambda 与 std::function 的性能对比七、完整例子回调系统八、Lambda 取代传统函数指针九、Lambda 与 STL 算法十、常见错误1. 捕获引用导致悬空引用2. 默认捕获 [] 捕获 this3. 在循环中捕获引用变量4. std::function 赋值开销大十一、这一篇的收获一、Lambda 的基本语法cpp[capture](parameters) - return_type { body }最简单形式cppauto greet []() { cout Hello endl; }; greet(); // 调用 // 带参数 auto add [](int a, int b) { return a b; }; cout add(3, 5); // 8 // 指定返回类型通常可省略 auto divide [](double a, double b) - double { if (b 0) return 0; return a / b; };二、捕获列表Capture捕获列表定义了 Lambda 如何访问外部变量。值捕获 []cppint x 10, y 20; auto add []() { return x y; }; // 拷贝 x, y cout add(); // 30 x 100; // 不影响 Lambda 内部的值 cout add(); // 仍然是 30引用捕获 []cppint x 10, y 20; auto add []() { return x y; }; cout add(); // 30 x 100; // Lambda 内部也会变 cout add(); // 120混合捕获cppint a 1, b 2, c 3; auto func [a, b](int x) { // a 值捕获b 引用捕获c 不捕获 // return a b c; // ❌ c 不可用 return a b x; };特定变量捕获cppint x 10, y 20, z 30; auto f1 [x]() { return x; }; // 只捕获 x auto f2 [y]() { return y; }; // 只捕获 y引用 auto f3 [x, y]() { return x y; }; // x 值y 引用C14 广义捕获带初始值cppint x 10; auto f [y x 5]() { return y; }; // y 用表达式初始化 cout f(); // 15 auto g [ptr make_uniqueint(42)]() { return *ptr; };C17 捕获 *this在成员函数中捕获*this的值而非指针cppclass Widget { int value 10; public: void memberFunc() { auto f1 [this]() { return value; }; // 捕获 this 指针 auto f2 [*this]() { return value; }; // C17拷贝整个对象 // f2 修改不影响原对象 } };三、mutable 关键字默认情况下Lambda 的operator()是const不能修改值捕获的变量。加mutable可修改副本cppint count 0; auto increment [count]() mutable { return count; // 修改的是 Lambda 内部的副本 }; cout increment(); // 1 cout increment(); // 2 cout count; // 0原变量不变四、泛型 LambdaC14参数可以用auto让 Lambda 成为模板cppauto add [](auto a, auto b) { return a b; }; cout add(3, 5); // 8 cout add(3.14, 2.86); // 6.0 cout add(string(a), b); // ab相当于编译器生成多个版本的operator()重载。五、std::function统一的可调用对象包装器std::function可以存储任何可调用对象函数指针、Lambda、函数对象、成员函数。cpp#include functional // 存储函数指针 int add(int a, int b) { return a b; } std::functionint(int, int) f1 add; // 存储 Lambda std::functionint(int, int) f2 [](int a, int b) { return a * b; }; // 存储函数对象 struct Multiply { int operator()(int a, int b) const { return a * b; } }; std::functionint(int, int) f3 Multiply(); // 使用 cout f1(3, 5); // 8 cout f2(3, 5); // 15存储成员函数成员函数需要一个对象实例cppstruct Calculator { int add(int a, int b) const { return a b; } }; Calculator calc; // 使用 std::bind 或 Lambda std::functionint(int, int) f [calc](int a, int b) { return calc.add(a, b); }; // 或用 std::mem_fn #include functional std::functionint(const Calculator, int, int) f2 Calculator::add;六、Lambda 与 std::function 的性能对比特性原始 Lambdastd::function 包装类型唯一匿名类型类型擦除后的通用类型内存栈上大小捕获大小堆上可能分配取决于大小调用开销可内联间接调用虚函数风格适用场景性能敏感局部使用需要存储、传递时结论能直接用auto就用auto只有在需要存储不同类型可调用对象时才用std::function。cpp// 推荐 auto lambda [](int x) { return x * 2; }; // 仅当需要统一类型容器时 vectorfunctionint(int) callbacks; callbacks.push_back([](int x) { return x 1; }); callbacks.push_back([](int x) { return x * 2; });七、完整例子回调系统cpp#include iostream #include vector #include functional #include string #include algorithm using namespace std; // 事件类型 enum class Event { Click, KeyPress, MouseMove }; // 事件管理器存储不同事件的回调 class EventManager { private: using Callback functionvoid(const string); vectorpairEvent, Callback handlers; public: void subscribe(Event e, Callback cb) { handlers.emplace_back(e, move(cb)); } void fire(Event e, const string data) { for (const auto [event, callback] : handlers) { if (event e) { callback(data); } } } }; int main() { EventManager em; // 注册 Lambda 回调 em.subscribe(Event::Click, [](const string data) { cout [Click 处理器 1] data endl; }); em.subscribe(Event::Click, [](const string data) { cout [Click 处理器 2] 收到点击: data endl; }); em.subscribe(Event::KeyPress, [](const string key) { cout [按键处理器] 按下: key endl; }); // 存储有状态的 Lambda int clickCount 0; em.subscribe(Event::Click, [clickCount](const string data) { clickCount; cout [计数器] 点击次数: clickCount endl; }); // 触发事件 cout 触发 Click 事件 endl; em.fire(Event::Click, 按钮被按下); cout \n 触发 KeyPress 事件 endl; em.fire(Event::KeyPress, Enter); return 0; }输出text 触发 Click 事件 [Click 处理器 1] 按钮被按下 [Click 处理器 2] 收到点击: 按钮被按下 [计数器] 点击次数: 1 触发 KeyPress 事件 [按键处理器] 按下: Enter八、Lambda 取代传统函数指针传统 C 风格回调cpp// 传统方式需要定义全局函数或 static 成员 int compare_int(const void* a, const void* b) { return *(int*)a - *(int*)b; } qsort(arr, n, sizeof(int), compare_int);现代 C 方式cppvectorint v {3, 1, 4, 1, 5}; // 使用 Lambda sort(v.begin(), v.end(), [](int a, int b) { return a b; }); // 自定义复杂排序 sort(v.begin(), v.end(), [](int a, int b) { // 按绝对值降序绝对值相同时按原值降序 int abs_a abs(a), abs_b abs(b); if (abs_a ! abs_b) return abs_a abs_b; return a b; });优势逻辑定义在调用点可读性好可以捕获局部变量不需要全局数据编译器可内联性能更好九、Lambda 与 STL 算法cpp#include algorithm #include vector #include numeric using namespace std; // 查找第一个满足条件的元素 vectorint v {1, 2, 3, 4, 5, 6}; auto it find_if(v.begin(), v.end(), [](int x) { return x 3 x % 2 0; }); // 找到 4 // 转换每个元素 transform(v.begin(), v.end(), v.begin(), [](int x) { return x * 2; }); // 条件删除 v.erase(remove_if(v.begin(), v.end(), [](int x) { return x 5; }), v.end()); // 累加时处理每个元素 int sum accumulate(v.begin(), v.end(), 0, [](int acc, int x) { return acc x * x; // 平方和 }); // 生成数据 generate_n(back_inserter(v), 10, [n 0]() mutable { return n 2; });十、常见错误1. 捕获引用导致悬空引用cppfunctionint() createCounter() { int local 0; return [local]() { return local; }; // ❌ 返回后 local 销毁 } // 使用时会访问已销毁的内存修正值捕获[]或[local]。2. 默认捕获 [] 捕获 thiscppclass Widget { int x 10; public: auto getLambda() { return []() { return x; }; // [] 捕获了 this 指针不是 x 的副本 } };修正C17 可用[*this]拷贝整个对象或显式捕获[x]。3. 在循环中捕获引用变量cppvectorfunctionint() funcs; for (int i 0; i 5; i) { funcs.push_back([i]() { return i; }); // ❌ 所有 Lambda 共享同一个 i } for (auto f : funcs) cout f() ; // 输出 5 5 5 5 5修正值捕获[i]。4. std::function 赋值开销大std::function可能会分配堆内存。频繁复制大的function对象可能成为性能瓶颈。十一、这一篇的收获你现在应该理解Lambda 语法[capture](params) mutable - ret { body }捕获方式[]值、[]引用、[this]、[*this]C17泛型 Lambda[](auto x, auto y)参数用autostd::function类型擦除包装器可存储任何可调用对象性能优先用auto保存 Lambda需要统一类型时才用std::function 小作业实现一个TaskScheduler类支持延迟执行和周期性执行任务存储std::functionvoid()回调。用 Lambda 注册任务测试单个任务和循环任务。下一篇预告第49篇《面向对象的单元测试用GoogleTest测试类》——如何用 GoogleTest 测试 C 类测试夹具Test Fixture复用对象EXPECT_*vsASSERT_*以及如何 mock 虚接口。下篇讲 OOP 代码的测试实践。