“把函数当值用"的三种写法:函数指针、std::function、lambda。包含语法拆解、三者对比、捕获方式速记与常见坑。

核心概念速查

概念说明关键点
函数指针存函数地址的变量C 风格,语法绕
std::function通用函数包装器C++11,可存 lambda/仿函数
lambda匿名函数对象C++11,现代首选
回调函数指针的典型用途事件、排序、遍历
捕获lambda 使用外部变量[ ] [&] [=] [x]

为什么需要函数指针

把"行为"作为参数传递,让代码更灵活。

// 没有函数指针: 每有一种排序规则就要写一个函数
void sort_ascending(int* arr, int n);
void sort_descending(int* arr, int n);

// 有函数指针: 一个排序函数,规则由调用方决定
void sort(int* arr, int n, bool (*compare)(int, int));

函数指针基础语法

#include <iostream>

// 定义两个比较函数
bool ascending(int a, int b)  { return a < b; }
bool descending(int a, int b) { return a > b; }

// 参数是函数指针: bool (*compare)(int, int)
void sort(int* arr, int n, bool (*compare)(int, int)) {
    for (int i = 0; i < n - 1; ++i)
        for (int j = i + 1; j < n; ++j)
            if (compare(arr[j], arr[i]))   // 调用比较函数
                std::swap(arr[i], arr[j]);
}

int main() {
    int arr[] = {3, 1, 4, 1, 5};
    sort(arr, 5, ascending);    // 传函数名(自动转成指针)
    // sort(arr, 5, &ascending); // 显式取地址也可以

    sort(arr, 5, descending);
    for (int x : arr) std::cout << x << " ";
    return 0;
}

语法记忆:bool (*compare)(int, int) —— *compare 是函数名,(int,int) 是参数,bool 是返回类型。不加括号 bool *compare(...) 就变成返回指针的函数了。

函数指针 vs std::function vs lambda

特性函数指针std::functionlambda
C++ 版本全版本C++11C++11
语法繁琐稍简最简洁
捕获变量❌ 不能✅ 可以✅ 可以
存成员函数❌ 麻烦✅ (bind)✅ (捕获this)
开销最小有额外包装可零开销
现代推荐旧代码见通用回调首选

lambda 详解

#include <iostream>

int main() {
    int base = 10;

    // [捕获方式] (参数) -> 返回类型 { 函数体 }
    auto add_base = [base](int x) -> int {
        return x + base;   // 捕获了 base 的拷贝
    };

    auto add_by_ref = [&base](int x) -> int {
        base += x;         // 引用捕获,修改外部变量
        return base;
    };

    auto everything = [=](int x) { return x + base; };   // 按值捕获全部
    auto modify_all = [&](int x) { base += x; return base; }; // 按引用捕获全部

    // 空捕获 []: 只能访问参数和自身
    auto pure = [](int x) { return x * 2; };

    std::cout << add_base(5) << "\n";   // 15
    std::cout << modify_all(5) << "\n"; // 15
    return 0;
}

捕获方式速记:

写法含义用途
[]不捕获纯函数,最安全
[x]按值捕 x只读外部单个变量
[&x]按引用捕 x要修改外部变量
[=]按值捕全部简洁但可能有拷贝开销
[&]按引用捕全部用全部外部变量
[=, &x]混合默认值捕,特殊引用

std::function 通用回调

#include <iostream>
#include <functional>

// std::function 可以作为成员/参数,存任意"可调用对象"
void register_callback(std::function<void(int)> cb) {
    cb(42);   // 调用时不知道背后是函数/lambda/仿函数
}

void plain_func(int x) { std::cout << "func: " << x << "\n"; }

int main() {
    std::function<void(int)> f1 = plain_func;  // 存普通函数
    std::function<void(int)> f2 = [](int x) { std::cout << "lambda: " << x << "\n"; };

    register_callback(f1);
    register_callback(f2);
    return 0;
}

常见坑

问题原因解决
lambda 按引用捕获生命期[&] 捕获了栈变量,回调时已析构按值捕获 / 确保变量存活
野指针回调悬空的 this / 已销毁对象捕获前确认生命期,用 weak_ptr
函数指针语法读不懂* 和括号优先级多练、用 std::function 或 alias
默认参数不适用函数指针不携带默认参数包一层 lambda
递归 lambda匿名无法自引用用 std::function 存自己

顿悟

  • 函数指针的本质是"代码的地址”:把一段逻辑当数据传来传去,政策和策略分离。
  • lambda 是现代 C++ 的回调主力:它其实是编译器帮你生成的匿名类 + operator(),捕获的变量就是类成员。
  • 从函数指针到 lambda 的演进:语法糖越来越多,但底层思想(回调、把行为当参数)始终没变。
  • 优先级是最大障碍:bool (*f)(int) 中括号让 *f 先绑定成"函数名",是无数人栽跟头的地方。

自测清单

  • 能写出并调用函数指针,看懂 bool (*)(int, int) 语法
  • 理解数组 + 函数指针 = 排序比较器用法
  • 会用 lambda,区分 [] [=] [&] [x]
  • 会用 std::function 存函数/lambda
  • 理解回调的应用场景(网络、事件、算法)
  • 知道 lambda 按引用捕获的生命期陷阱