C++ 回调函数(转载)

转载自:https://blog.csdn.net/weixin_73527660/article/details/156298635

作者:我的offer在哪里

理论知识

一、回调监听函数的核心概念

1. 定义

回调监听函数(简称回调函数):是一种「先注册、后触发」的函数,指你将函数的地址(指针)传递给某个模块(如框架、第三方库、自定义逻辑),当特定事件(如异步操作完成、状态变更、定时器到期)发生时,该模块会主动调用这个函数来通知你(完成 “监听” 效果)。

2. 核心特性

  • 反向调用:不是你主动调用函数,而是被其他模块被动触发;
  • 解耦性:调用方和被调用方无需直接依赖,通过函数接口通信;
  • 灵活性:可动态注册不同的回调函数,适配不同业务逻辑。

二、C++ 回调函数的 4 种实现方式(按常用程度排序)

方式 1:函数指针(C 风格,最基础)

函数指针是 C++ 回调的基础,通过存储函数地址实现回调,适合简单场景(无状态、全局 / 静态函数)。

代码示例:事件监听回调
#include <iostream>
#include <string>
 
// 1. 定义回调函数类型(简化函数指针声明)
typedef void (*EventCallback)(const std::string& eventName, int eventId);
 
// 2. 回调函数实现(符合回调类型签名)
void OnButtonClick(const std::string& eventName, int eventId) {
    std::cout << "监听到按钮点击事件:" << eventName << ",事件ID:" << eventId << std::endl;
}
 
void OnTimerTimeout(const std::string& eventName, int eventId) {
    std::cout << "监听到定时器超时事件:" << eventName << ",事件ID:" << eventId << std::endl;
}
 
// 3. 事件管理器(负责注册和触发回调)
class EventManager {
public:
    // 注册回调函数
    void registerCallback(EventCallback cb) {
        m_callback = cb;
    }
 
    // 模拟事件发生,触发回调
    void triggerEvent(const std::string& eventName, int eventId) {
        if (m_callback != nullptr) {
            m_callback(eventName, eventId); // 调用回调函数
        } else {
            std::cout << "未注册回调函数" << std::endl;
        }
    }
 
private:
    EventCallback m_callback = nullptr; // 存储回调函数地址
};
 
// 测试
int main() {
    EventManager manager;
 
    // 注册按钮点击回调
    manager.registerCallback(OnButtonClick);
    manager.triggerEvent("按钮点击", 1001);
 
    // 切换为定时器超时回调
    manager.registerCallback(OnTimerTimeout);
    manager.triggerEvent("定时器超时", 2001);
 
    return 0;
}
特点
  • 优点:简单高效、兼容性好(兼容 C 语言);
  • 缺点:仅支持全局 / 静态函数,无法捕获类成员变量(无状态),不支持灵活的参数绑定。

方式 2:类成员函数指针(面向对象场景)

在 C++ 类中,成员函数有隐含的 this 指针,无法直接用普通函数指针存储,需使用「类成员函数指针」实现回调,适合监听类内部事件。

代码示例:类成员函数回调

#include <iostream>
#include <string>
 
// 事件监听类
class EventListener {
public:
    // 类成员回调函数
    void OnNetworkSuccess(const std::string& msg) {
        std::cout << "类内监听:网络请求成功,消息:" << msg << std::endl;
    }
 
    void OnNetworkFailed(const std::string& msg) {
        std::cout << "类内监听:网络请求失败,消息:" << msg << std::endl;
    }
};
 
// 网络管理器
class NetworkManager {
public:
    // 定义类成员函数指针类型(需指定类名)
    typedef void (EventListener::*NetworkCallback)(const std::string& msg);
 
    // 注册回调(需传递对象实例和成员函数指针)
    void registerCallback(EventListener* listener, NetworkCallback cb) {
        m_listener = listener;
        m_callback = cb;
    }
 
    // 模拟网络请求完成,触发回调
    void requestData(const std::string& url) {
        std::cout << "正在请求:" << url << std::endl;
        // 模拟请求成功
        if (m_listener != nullptr && m_callback != nullptr) {
            // 调用类成员函数指针(必须通过对象实例调用)
            (m_listener->*m_callback)("数据加载完成");
        }
    }
 
private:
    EventListener* m_listener = nullptr;
    NetworkCallback m_callback = nullptr;
};
 
// 测试
int main() {
    EventListener listener;
    NetworkManager netManager;
 
    // 注册类成员回调
    netManager.registerCallback(&listener, &EventListener::OnNetworkSuccess);
    netManager.requestData("https://example.com/data");
 
    // 切换为失败回调
    netManager.registerCallback(&listener, &EventListener::OnNetworkFailed);
    netManager.requestData("https://example.com/error");
 
    return 0;
}
特点
  • 优点:支持类成员函数,可访问类的成员变量(有状态);
  • 缺点:语法繁琐(需指定类名),只能绑定单个类的成员函数,灵活性不足。

方式 3:std::function + std::bind(C++11 及以上,推荐)

std::function 是 C++11 提供的通用函数包装器,可存储任意可调用对象(普通函数、成员函数、lambda 表达式等),配合 std::bind 可绑定类成员函数和参数,是最灵活的回调实现方式。

代码示例:灵活的回调绑定
#include <iostream>
#include <string>
#include <functional> // 包含std::function和std::bind
 
// 回调类型定义(std::function包装,支持任意可调用对象)
using NotifyCallback = std::function<void(const std::string&, int)>;
 
// 消息通知器
class Notifier {
public:
    void registerCallback(NotifyCallback cb) {
        m_callback = std::move(cb); // 移动语义,提高效率
    }
 
    void sendNotify(const std::string& title, int priority) {
        std::cout << "准备发送通知..." << std::endl;
        if (m_callback) { // std::function可直接判断是否有效
            m_callback(title, priority); // 触发回调
        }
    }
 
private:
    NotifyCallback m_callback;
};
 
// 业务类(包含成员函数)
class BusinessService {
public:
    void onNotifyReceived(const std::string& title, int priority, const std::string& extra) {
        std::cout << "业务服务收到通知:" << title 
                  << ",优先级:" << priority 
                  << ",附加信息:" << extra << std::endl;
    }
};
 
// 测试
int main() {
    Notifier notifier;
    BusinessService business;
 
    // 1. 绑定普通函数
    auto normalFunc = [](const std::string& title, int priority) {
        std::cout << "普通Lambda回调:" << title << ",优先级:" << priority << std::endl;
    };
    notifier.registerCallback(normalFunc);
    notifier.sendNotify("系统公告", 1);
 
    // 2. 绑定类成员函数(用std::bind绑定this和额外参数)
    auto memberFunc = std::bind(&BusinessService::onNotifyReceived, &business,
                                 std::placeholders::_1, // 对应第一个参数(title)
                                 std::placeholders::_2, // 对应第二个参数(priority)
                                 "来自业务模块"); // 额外绑定的固定参数
    notifier.registerCallback(memberFunc);
    notifier.sendNotify("业务告警", 2);
 
    // 3. 直接绑定带捕获的Lambda(最简洁)
    std::string user = "张三";
    notifier.registerCallback([&user](const std::string& title, int priority) {
        std::cout << "用户" << user << "收到通知:" << title << ",优先级:" << priority << std::endl;
    });
    notifier.sendNotify("个人消息", 3);
 
    return 0;
}
特点
  • 优点:
    1. 支持任意可调用对象(普通函数、成员函数、lambda、函数对象);
    2. 可绑定额外参数,支持捕获 lambda 的上下文变量;
    3. 语法简洁,类型安全,是 C++ 现代开发的首选;
  • 缺点:需 C++11 及以上版本支持,少量性能开销(可忽略,满足绝大多数场景)。

方式 4:函数对象(仿函数,适用于复杂逻辑)

函数对象是重载了 operator() 的类 / 结构体,可存储状态(成员变量),适合回调逻辑复杂、需要复用状态的场景。

代码示例:函数对象回调

#include <iostream>
#include <string>
 
// 函数对象(仿函数):日志回调器
class LogCallback {
public:
    // 构造函数:初始化日志级别
    LogCallback(const std::string& level) : m_logLevel(level) {}
 
    // 重载operator(),作为回调入口
    void operator()(const std::string& content) {
        std::cout << "[" << m_logLevel << "] " << content << std::endl;
    }
 
private:
    std::string m_logLevel; // 存储状态(日志级别)
};
 
// 日志管理器
class LogManager {
public:
    using LogFunc = LogCallback; // 函数对象类型
 
    void setLogCallback(LogFunc cb) {
        m_logCb = std::move(cb);
    }
 
    void log(const std::string& content) {
        m_logCb(content); // 调用函数对象
    }
 
private:
    LogFunc m_logCb;
};
 
// 测试
int main() {
    LogManager logManager;
 
    // 注册INFO级别日志回调
    logManager.setLogCallback(LogCallback("INFO"));
    logManager.log("程序启动成功");
 
    // 注册ERROR级别日志回调
    logManager.setLogCallback(LogCallback("ERROR"));
    logManager.log("文件读取失败");
 
    return 0;
}

 特点 
  • 优点:可存储状态(无需依赖外部变量),逻辑封装性好,适合复杂回调场景;
  • 缺点:语法比 lambda 繁琐,灵活性略低于 std::function

三、回调监听函数的典型使用场景

  1. 异步操作通知:如网络请求完成、文件读写结束、线程任务执行完毕后的结果回调;
  2. 事件监听:如 UI 按钮点击、定时器超时、状态变更(如数据更新)的事件响应;
  3. 框架扩展:如第三方库(如 OpenCV、Qt)的回调接口(如 Qt 的信号槽本质是回调的封装);
  4. 算法回调:如排序算法的自定义比较函数、遍历算法的元素处理回调。

四、使用回调函数的注意事项

  1. 生命周期管理:确保回调函数所依赖的对象(如类实例)在回调触发时未被销毁(避免野指针 / 悬空引用);
  2. 线程安全:若回调在多线程环境下触发,需保证回调函数内的操作线程安全(如加锁);
  3. 避免回调嵌套过深:过多回调嵌套会导致 “回调地狱”,可通过 Promise/Future(C++11 及以上)优化;
  4. 类型匹配:回调函数的参数类型、返回值类型必须与注册接口的要求一致,否则会编译报错。

五、总结

  1. 回调监听函数是「先注册、后触发」的被动调用机制,核心作用是解耦和实现事件驱动;
  2. 实现方式优先级:std::function + lambda(C++11 + 首选)> 类成员函数指针 > 函数指针 > 函数对象;
  3. 关键技巧:std::bind 用于绑定类成员函数和固定参数,lambda 用于简洁捕获上下文,std::function 提供通用包装;
  4. 注意事项:重点关注对象生命周期和线程安全,避免悬空引用和数据竞争。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值