C++函数回调方式
·
在 C++ 中,类与类之间的函数回调是实现类间通信的重要方式(例如 A 类触发事件后通知 B 类处理)。根据类之间的关系和使用场景,主要有以下几种实现方式:
一、通过函数指针(全局函数/静态成员函数)作为桥梁
这是最基础的方式:一个类(被回调方)提供全局函数或静态成员函数作为回调入口,另一个类(调用方)通过函数指针传递回调,并在触发时调用。
示例:B 类回调 A 类的方法
#include <iostream>
// 被回调的类
class A {
public:
void handleEvent(int data) {
std::cout << "A 处理事件,数据: " << data << std::endl;
}
// 静态成员函数(作为回调桥梁)
static void staticCallback(A* instance, int data) {
if (instance) {
instance->handleEvent(data); // 调用非静态成员
}
}
};
// 发起回调的类
class B {
public:
// 存储函数指针(参数包含A的实例指针)
using Callback = void (*)(A*, int);
Callback callback_;
A* a_instance_;
// 注册回调(关联A的实例和静态函数)
void setCallback(A* a, Callback cb) {
a_instance_ = a;
callback_ = cb;
}
// 触发回调
void triggerEvent(int data) {
if (callback_) {
callback_(a_instance_, data); // 调用A的回调
}
}
};
int main() {
A a;
B b;
b.setCallback(&a, &A::staticCallback); // 注册回调
b.triggerEvent(100); // 触发,输出:A 处理事件,数据: 100
return 0;
}
优点
- 简单直接,兼容性好(可与 C 代码交互)。
- 性能开销小,适合对性能敏感的场景。
缺点
- 需要静态成员函数作为中间层,不够直观。
- 函数指针类型固定,灵活性低(若回调参数变化,需修改多处)。
二、通过 std::function + lambda(现代 C++ 推荐)
利用 std::function 包装回调逻辑,配合 lambda 表达式捕获类实例,实现类间回调的灵活性。
示例:B 类回调 A 类,支持任意签名
#include <iostream>
#include <functional>
// 被回调的类
class A {
public:
void onMessage(const std::string& msg) {
std::cout << "A 收到消息: " << msg << std::endl;
}
};
// 发起回调的类
class B {
public:
// 用std::function存储回调(支持任意可调用对象)
using Callback = std::function<void(const std::string&)>;
Callback callback_;
// 注册回调
void setCallback(Callback cb) {
callback_ = cb;
}
// 触发回调
void sendMessage(const std::string& msg) {
if (callback_) {
callback_(msg); // 调用回调
}
}
};
int main() {
A a;
B b;
// 注册回调:lambda捕获A的实例,调用其成员函数
b.setCallback([&a](const std::string& msg) {
a.onMessage(msg);
});
b.sendMessage("Hello from B"); // 触发,输出:A 收到消息: Hello from B
return 0;
}
优点
- 灵活性极高:支持任意函数签名,无需静态成员函数。
- 代码简洁:通过 lambda 直接捕获类实例,无需显式传递
this指针。 - 类型安全:编译期检查回调签名是否匹配。
缺点
- 存在轻微的性能开销(类型擦除机制),但远小于代码维护成本。
- 依赖 C++11 及以上标准库。
三、通过接口(纯虚函数)实现回调(观察者模式)
定义一个抽象接口类,被回调的类实现该接口,调用方通过接口指针触发回调。这是设计模式中“观察者模式”的典型实现。
示例:基于接口的观察者模式
#include <iostream>
#include <vector>
// 回调接口(纯虚函数)
class ICallback {
public:
virtual void onEvent(int code) = 0; // 纯虚函数,子类必须实现
virtual ~ICallback() = default; // 虚析构
};
// 被回调的类(实现接口)
class A : public ICallback {
public:
void onEvent(int code) override { // 实现接口方法
std::cout << "A 处理事件,代码: " << code << std::endl;
}
};
// 另一个被回调的类(也实现接口)
class C : public ICallback {
public:
void onEvent(int code) override {
std::cout << "C 处理事件,代码: " << code << std::endl;
}
};
// 发起回调的类(可通知多个观察者)
class B {
private:
std::vector<ICallback*> observers_; // 存储接口指针
public:
// 注册观察者(添加回调)
void addObserver(ICallback* observer) {
observers_.push_back(observer);
}
// 触发事件,通知所有观察者
void trigger(int code) {
for (auto obs : observers_) {
obs->onEvent(code); // 调用接口方法(多态)
}
}
};
int main() {
A a;
C c;
B b;
b.addObserver(&a); // A 注册为观察者
b.addObserver(&c); // C 注册为观察者
b.trigger(200); // 触发事件,A和C都会被回调
// 输出:
// A 处理事件,代码: 200
// C 处理事件,代码: 200
return 0;
}
优点
- 规范性强:通过接口定义明确回调契约,符合面向对象设计原则。
- 支持多观察者:可同时回调多个实现了接口的类(一对多通信)。
- 类型安全:编译期确保回调方法存在,无函数指针的类型风险。
缺点
- 需定义接口类,略增加代码量。
- 回调签名固定(接口方法的签名),修改需调整接口。
四、通过成员函数指针直接回调
C++ 支持指向类成员函数的指针(T::* 语法),可直接调用类的非静态成员函数,但需要显式传递实例指针。
示例:使用成员函数指针
#include <iostream>
class A {
public:
void func(int data) {
std::cout << "A::func 被调用,数据: " << data << std::endl;
}
};
class B {
public:
// 定义A的成员函数指针类型
using AMemberFunc = void (A::*)(int);
// 存储A的实例和成员函数指针
A* a_instance_;
AMemberFunc a_func_;
// 注册回调(绑定A的实例和成员函数)
void setCallback(A* a, AMemberFunc func) {
a_instance_ = a;
a_func_ = func;
}
// 触发回调
void invoke(int data) {
if (a_instance_ && a_func_) {
(a_instance_->*a_func_)(data); // 调用成员函数指针
}
}
};
int main() {
A a;
B b;
b.setCallback(&a, &A::func); // 注册A的成员函数
b.invoke(300); // 输出:A::func 被调用,数据: 300
return 0;
}
优点
- 直接关联非静态成员函数,无需静态中间层。
- 性能好,无额外开销。
缺点
- 语法复杂(
(a->*func)(...)调用方式不直观)。 - 成员函数指针与类强绑定(只能用于特定类的回调),通用性差。
各类方式对比与选择建议
| 方式 | 灵活性 | 易用性 | 适用场景 |
|---|---|---|---|
| 函数指针 + 静态成员 | 低 | 中 | 需兼容C代码、简单类间通信 |
std::function + lambda |
高 | 高 | 现代C++主流场景,灵活的类间回调 |
| 接口(观察者模式) | 中 | 中 | 多对多通信、需要明确接口契约的框架设计 |
| 成员函数指针 | 低 | 低 | 仅用于固定类之间的回调,且追求极致性能 |
总结
- 优先推荐:
std::function + lambda,兼顾灵活性和简洁性,适合大多数类间回调场景。 - 框架设计:采用接口(观察者模式),通过抽象定义明确类间交互契约。
- 兼容性需求:使用函数指针 + 静态成员函数,确保与C代码或旧系统兼容。
- 极少推荐:直接使用成员函数指针,除非有明确的性能需求且类关系固定。
更多推荐


所有评论(0)