C++ 回调函数详解

C++ 回调函数详解

什么是回调函数?

回调函数(Callback Function)是一种作为参数传递给另一个函数的函数。换句话说,就是把一个函数当作参数传给另一个函数,让后者在适当的时机去"调用"这个函数。

简单比喻

想象你打电话给客服说"等会有空回我",客服说"好的"。这里的"回电"就是一个回调------你把要执行的操作(回电)留给对方在合适的时候执行。

在编程中,回调函数就是把自己的代码交给别人去调用


回调函数的作用

  1. 异步执行 - 不阻塞主线程,让耗时的操作完成后自动执行后续代码
  2. 事件驱动 - 当某个事件发生时(如点击按钮),自动调用相应的处理函数
  3. 代码解耦 - 调用者不需要知道被调用者的具体实现,只需要知道接口
  4. 策略模式 - 可以在运行时动态改变行为

C++ 中的回调函数实现方式

C++ 中实现回调函数主要有几种方式:

  1. 函数指针
  2. std::function 和 std::bind
  3. Lambda 表达式
  4. 虚函数/接口

方式一:函数指针

基本语法

cpp 复制代码
#include <iostream>
#include <vector>

// 定义函数指针类型
typedef int (*Operation)(int, int);
// 或者使用 using
using Operation = int(*)(int, int);

// 使用函数指针的函数
int calculate(int a, int b, Operation op) {
    return op(a, b);
}

// 具体的回调函数
int add(int a, int b) {
    return a + b;
}

int multiply(int a, int b) {
    return a * b;
}

int main() {
    // 传递函数指针作为回调
    std::cout << "加法: " << calculate(10, 5, add) << std::endl;       // 15
    std::cout << "乘法: " << calculate(10, 5, multiply) << std::endl;  // 50
    return 0;
}

示例:排序比较函数

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>

// 比较函数的返回类型必须是 bool
bool ascending(int a, int b) {
    return a < b;  // 升序排列
}

bool descending(int a, int b) {
    return a > b;  // 降序排列
}

int main() {
    std::vector<int> nums = {5, 2, 8, 1, 9};

    // 使用函数指针进行排序
    std::sort(nums.begin(), nums.end(), ascending);

    std::cout << "升序: ";
    for (int n : nums) std::cout << n << " ";  // 1 2 5 8 9
    std::cout << std::endl;

    // 重新排序为降序
    std::sort(nums.begin(), nums.end(), descending);

    std::cout << "降序: ";
    for (int n : nums) std::cout << n << " ";  // 9 8 5 2 1
    std::cout << std::endl;

    return 0;
}

方式二:std::function 和 std::bind

基本用法

cpp 复制代码
#include <iostream>
#include <functional>

// 使用 std::function 作为参数
void processNumber(int num, std::function<void(int)> callback) {
    std::cout << "处理数字: " << num << std::endl;
    if (callback) {
        callback(num * 2);  // 执行回调
    }
}

void myCallback(int result) {
    std::cout << "回调结果: " << result << std::endl;
}

int main() {
    // 传递普通函数
    processNumber(10, myCallback);

    // 传递 Lambda 表达式
    processNumber(20, [](int result) {
        std::cout << "Lambda 回调: " << result << std::endl;
    });

    return 0;
}

std::bind 绑定参数

cpp 复制代码
#include <iostream>
#include <functional>

void greet(std::string name, std::string message) {
    std::cout << message << ", " << name << "!" << std::endl;
}

int main() {
    // 使用 bind 绑定部分参数
    auto sayHello = std::bind(greet, std::placeholders::_1, "Hello");
    auto sayBye = std::bind(greet, std::placeholders::_1, "Goodbye");

    sayHello("Alice");  // Hello, Alice!
    sayBye("Bob");      // Goodbye, Bob!

    return 0;
}

方式三:Lambda 表达式(推荐)

Lambda 是 C++11 引入的特性,是现代 C++ 中最常用的回调方式。

基本语法

cpp 复制代码
[capture list](parameters) -> return_type {
    // function body
}

示例一:基础用法

cpp 复制代码
#include <iostream>
#include <vector>

void forEach(std::vector<int>& nums, std::function<void(int)> func) {
    for (int n : nums) {
        func(n);
    }
}

int main() {
    std::vector<int> nums = {1, 2, 3, 4, 5};

    // 使用 Lambda 作为回调
    forEach(nums, [](int n) {
        std::cout << n << " ";
    });
    std::cout << std::endl;

    return 0;
}

示例二:捕获外部变量

cpp 复制代码
#include <iostream>
#include <vector>

int main() {
    int multiplier = 2;

    // Lambda 捕获外部变量
    auto multiply = [multiplier](int x) {
        return x * multiplier;
    };

    std::cout << multiply(5) << std::endl;  // 10

    // 修改捕获方式
    int counter = 0;
    auto increment = [&counter]() {
        counter++;
    };

    increment();
    increment();
    std::cout << counter << std::endl;  // 2

    return 0;
}

捕获方式说明:

捕获方式 说明
[x] 按值捕获 x
[&x] 按引用捕获 x
[=] 按值捕获所有外部变量
[&] 按引用捕获所有外部变量
[=, &x] 按值捕获所有,除 x 按引用
[&, x] 按引用捕获所有,除 x 按值

示例三:作为排序回调

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

struct Person {
    std::string name;
    int age;
};

int main() {
    std::vector<Person> people = {
        {"Alice", 25},
        {"Bob", 30},
        {"Charlie", 20}
    };

    // 按年龄排序
    std::sort(people.begin(), people.end(),
        [](const Person& a, const Person& b) {
            return a.age < b.age;
        });

    std::cout << "按年龄排序:" << std::endl;
    for (const auto& p : people) {
        std::cout << p.name << ": " << p.age << std::endl;
    }

    return 0;
}

方式四:接口/虚函数(策略模式)

基础接口实现

cpp 复制代码
#include <iostream>
#include <vector>

// 定义策略接口
class IStrategy {
public:
    virtual ~IStrategy() = default;
    virtual int execute(int a, int b) = 0;
};

// 具体策略 A
class AddStrategy : public IStrategy {
public:
    int execute(int a, int b) override {
        return a + b;
    }
};

// 具体策略 B
class MultiplyStrategy : public IStrategy {
public:
    int execute(int a, int b) override {
        return a * b;
    }
};

// 使用策略的上下文
class Calculator {
private:
    IStrategy* strategy;
public:
    void setStrategy(IStrategy* s) {
        strategy = s;
    }

    int calculate(int a, int b) {
        return strategy->execute(a, b);
    }
};

int main() {
    Calculator calc;

    // 使用加法策略
    calc.setStrategy(new AddStrategy());
    std::cout << calc.calculate(10, 5) << std::endl;  // 15

    // 切换到乘法策略
    calc.setStrategy(new MultiplyStrategy());
    std::cout << calc.calculate(10, 5) << std::endl;  // 50

    return 0;
}

回调函数在实际项目中的应用

应用一:定时器回调

cpp 复制代码
#include <iostream>
#include <chrono>
#include <thread>
#include <functional>
#include <atomic>

class Timer {
private:
    std::atomic<bool> running{false};
    std::thread timerThread;
    std::function<void()> callback;

public:
    void start(int intervalMs, std::function<void()> cb) {
        running = true;
        callback = cb;

        timerThread = std::thread([this, intervalMs]() {
            while (running) {
                std::this_thread::sleep_for(std::chrono::milliseconds(intervalMs));
                if (callback && running) {
                    callback();
                }
            }
        });
    }

    void stop() {
        running = false;
        if (timerThread.joinable()) {
            timerThread.join();
        }
    }

    ~Timer() {
        stop();
    }
};

int main() {
    Timer timer;
    int counter = 3;

    timer.start(1000, [&counter]() {
        std::cout << "倒计时: " << counter << std::endl;
        counter--;
    });

    // 等待倒计时结束
    std::this_thread::sleep_for(std::chrono::seconds(4));
    timer.stop();

    std::cout << "时间到!" << std::endl;

    return 0;
}

应用二:按钮事件回调

cpp 复制代码
#include <iostream>
#include <functional>
#include <string>
#include <map>

// 简化的按钮类
class Button {
private:
    std::string text;
    std::function<void()> onClick;

public:
    Button(const std::string& t) : text(t) {}

    void setOnClick(std::function<void()> callback) {
        onClick = callback;
    }

    void click() {
        std::cout << "按钮 [" << text << "] 被点击" << std::endl;
        if (onClick) {
            onClick();
        }
    }
};

int main() {
    Button saveBtn("保存");
    Button cancelBtn("取消");

    // 设置点击回调
    saveBtn.setOnClick([]() {
        std::cout << "-> 执行保存操作" << std::endl;
    });

    cancelBtn.setOnClick([]() {
        std::cout << "-> 执行取消操作" << std::endl;
    });

    // 模拟点击
    saveBtn.click();
    cancelBtn.click();

    return 0;
}

应用三:异步任务回调

cpp 复制代码
#include <iostream>
#include <thread>
#include <functional>
#include <future>

// 模拟异步任务
void asyncTask(std::function<void(std::string)> callback) {
    std::thread([callback]() {
        std::this_thread::sleep_for(std::chrono::seconds(2));
        callback("任务完成! 结果: 42");
    }).detach();
}

int main() {
    std::cout << "开始异步任务..." << std::endl;

    // 注册回调函数
    asyncTask([](std::string result) {
        std::cout << "收到回调: " << result << std::endl;
    });

    std::cout << "主线程继续执行..." << std::endl;

    // 等待异步任务完成
    std::this_thread::sleep_for(std::chrono::seconds(3));

    return 0;
}

应用四:数据过滤回调

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>

// 使用模板和函数指针进行过滤
template<typename T>
std::vector<T> filterData(const std::vector<T>& data, bool (*filterFunc)(const T&)) {
    std::vector<T> result;
    for (const auto& item : data) {
        if (filterFunc(item)) {
            result.push_back(item);
        }
    }
    return result;
}

bool isEven(int n) {
    return n % 2 == 0;
}

bool isPositive(int n) {
    return n > 0;
}

int main() {
    std::vector<int> numbers = {-3, -2, -1, 0, 1, 2, 3, 4, 5};

    // 过滤偶数
    auto evens = filterData(numbers, isEven);
    std::cout << "偶数: ";
    for (int n : evens) std::cout << n << " ";  // -2 0 2 4
    std::cout << std::endl;

    // 过滤正数
    auto positives = filterData(numbers, isPositive);
    std::cout << "正数: ";
    for (int n : positives) std::cout << n << " ";  // 1 2 3 4 5
    std::cout << std::endl;

    // 使用 Lambda 过滤大于3的数
    auto greaterThan3 = filterData(numbers, [](int n) { return n > 3; });
    std::cout << "大于3: ";
    for (int n : greaterThan3) std::cout << n << " ";  // 4 5
    std::cout << std::endl;

    return 0;
}

应用五:遍历容器的回调

cpp 复制代码
#include <iostream>
#include <vector>
#include <string>

// 通用遍历函数
template<typename T>
void forEach(const std::vector<T>& items, std::function<void(const T&)> callback) {
    for (const auto& item : items) {
        callback(item);
    }
}

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5};
    std::vector<std::string> names = {"Alice", "Bob", "Charlie"};

    // 遍历数字
    std::cout << "数字: ";
    forEach(numbers, [](int n) { std::cout << n * n << " "; });
    std::cout << std::endl;

    // 遍历字符串
    std::cout << "名字: ";
    forEach(names, [](const std::string& name) {
        std::cout << "[" << name << "] ";
    });
    std::cout << std::endl;

    return 0;
}

回调函数的注意事项

1. 空指针检查

cpp 复制代码
#include <iostream>
#include <functional>

void process(std::function<void()> callback) {
    // 必须检查回调是否为空
    if (callback) {
        callback();
    }
    // 或者使用以下方式(更安全)
    // if (callback != nullptr) { ... }
}

int main() {
    process(nullptr);  // 不会崩溃

    process([]() {
        std::cout << "回调执行" << std::endl;
    });

    return 0;
}

2. Lambda 捕获生命周期

cpp 复制代码
#include <iostream>
#include <functional>

// 错误示例:返回捕获局部变量的 Lambda
std::function<int()> createCallback() {
    int local = 10;
    // 警告:local 是局部变量,按值捕获后返回的 Lambda 引用了即将销毁的变量
    return [local]() { return local; };
}

// 正确示例:按值捕获
std::function<int()> createCallbackCorrect() {
    int local = 10;
    return [local]() { return local; };  // local 的值被复制
}

// 正确示例:如果需要修改,使用 mutable
std::function<int()> createCounter() {
    int count = 0;
    return [&count]() mutable {
        ++count;
        return count;
    };
}

int main() {
    auto counter = createCounter();
    std::cout << counter() << std::endl;  // 1
    std::cout << counter() << std::endl;  // 2

    return 0;
}

3. 线程安全

cpp 复制代码
#include <iostream>
#include <thread>
#include <atomic>
#include <functional>

// 线程安全的回调执行
void safeCall(std::function<void()>& callback) {
    // 复制回调,在锁外执行,避免死锁
    auto localCallback = callback;  // 这里需要拷贝!
    if (localCallback) {
        localCallback();
    }
}

int main() {
    std::function<void()> callback;

    std::thread t1([&callback]() {
        callback = []() { std::cout << "Hello" << std::endl; };
    });

    std::thread t2([&callback]() {
        if (callback) {
            callback();
        }
    });

    t1.join();
    t2.join();

    return 0;
}

总结

实现方式 优点 缺点
函数指针 简单、直接 不支持捕获外部变量
std::function 灵活、可存储 有轻微性能开销
Lambda 语法简洁、可捕获变量 需要 C++11 以上
虚函数/接口 真正的多态、安全 代码较多

推荐 :现代 C++ 开发中,优先使用 Lambda 表达式 配合 std::function,它们是最灵活、最易读的回调实现方式。

相关推荐
退休倒计时5 分钟前
【每日一题】LeetCode 437. 路径总和 III TypeScript
算法·leetcode·typescript
老迟到的茉莉17 分钟前
Hermes 是谁?跟 Claude Code 差在哪
开发语言·python
学逆向的21 分钟前
汇编——内存
开发语言·汇编·算法·网络安全
tachibana227 分钟前
hot100 翻转二叉树(226)
java·数据结构·算法·leetcode
lbb 小魔仙1 小时前
MuMu 模拟器 6.0 硬核测评:这次升级确实不一样
java·开发语言·模拟器·mumu·硬核测评
r_oo_ki_e_1 小时前
Java 线程池
java·开发语言
生戎马1 小时前
高光谱拼接算法(七)USAC
算法
KuaCpp2 小时前
C++进程(下)
c++
杜子不疼.2 小时前
【Qt初识】信号槽(二):自定义信号函数与槽函数
开发语言·qt
PhotonixBay2 小时前
共聚焦成像核心原理:针孔、PSF与三维形貌测量技术
人工智能·测试工具·算法