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,它们是最灵活、最易读的回调实现方式。

相关推荐
会编程的土豆2 小时前
字符串知识(LCS,LIS)区分总结归纳
开发语言·数据结构·c++·算法
FuckPatience2 小时前
未能加载项目文件。名称不能以“<”字符(十六进制值 0x3C)开头
开发语言
书到用时方恨少!2 小时前
Python 面向对象编程:从“过程清单”到“智能积木”的思维革命
开发语言·python·面向对象
北顾笙9802 小时前
day25-数据结构力扣
数据结构·算法·leetcode
冰暮流星2 小时前
javascript案例-简易计算器
开发语言·javascript·ecmascript
Rsun045512 小时前
5、Java 原型模式从入门到实战
java·开发语言·原型模式
lxh01132 小时前
最接近的三数之和
java·数据结构·算法
天若有情6732 小时前
原创C++设计模式:功能归一化——无继承、轻量版AOP,比传统OOP更优雅
开发语言·c++·设计模式·oop
FrontAI2 小时前
Next.js从入门到实战保姆级教程:实战项目(上)——全栈博客系统架构与核心功能
开发语言·前端·javascript·react.js·系统架构