委托是类型安全的函数指针,可绑定普通函数、静态成员函数或类成员函数,用于事件通知与回调机制。C++中通过std::function和std::bind实现通用委托,支持封装各种可调用对象,如函数指针、lambda表达式及成员函数。使用std::function定义委托类型,结合std::vector存储多个回调,可实现多播委托,支持事件驱动编程。关键点包括:std::bind用于绑定对象实例与成员函数;std::weak_ptr避免悬空引用;自定义泛型Delegate类可模拟C#风格语法,支持赋值与调用操作符重载。现代C++推荐优先使用std::function配合lambda,因其简洁、高效且兼容性强,适用于GUI事件、定时器回调等场景。核心挑战在于生命周期管理,需确保被绑定对象在回调触发时仍有效。

在C++中实现一个通用的委托(Delegate),核心目标是将函数或成员函数封装成可回调的对象,支持事件驱动编程和观察者模式。由于C++没有内置委托机制(如C#),需要通过模板、函数对象、std::function 和 std::bind 或现代的lambda来模拟。
什么是委托?
委托是一种类型安全的函数指针,可以绑定到普通函数、静态成员函数或类成员函数,并在之后调用。它常用于事件通知、回调机制和解耦模块间依赖。
使用 std::function 和 std::bind 实现通用委托
C++11 提供了 std::function 和 std::bind,能轻松实现类型擦除的可调用对象包装,是最简单且通用的方式。
示例:基本回调绑定
#include#include #include // 定义委托类型:无参数,无返回值 using Delegate = std::function
; class Event { public: void AddListener(Delegate func) { listeners.push_back(func); }
void Notify() { for (auto& f : listeners) { f(); } }private: std::vector
listeners; }; class Handler { public: void OnEvent() { std::cout
int main() { Event evt; Handler h;
// 绑定成员函数 evt.AddListener(std::bind(&Handler::OnEvent, &h)); // 绑定 lambda evt.AddListener([]() { std::cout << "Lambda callback\n"; }); // 触发事件 evt.Notify(); return 0;}
支持多播委托(Multicast Delegate)
多播委托允许注册多个回调函数,依次调用。上面的 Event 类就是一个简单的多播委托实现。你可以扩展它支持移除监听器、线程安全等特性。
立即学习“C++免费学习笔记(深入)”;
关键点:
- std::function 能统一处理函数指针、成员函数、lambda。
- std::bind 将对象实例和成员函数绑定为可调用对象。
- 使用 std::weak_ptr 可避免因对象销毁导致的悬空引用(尤其在GUI或异步场景中)。
更高级:自定义泛型委托类
若想更贴近C#风格的委托语法,可封装一个泛型Delegate类,支持赋值、组合(+=)、调用等操作。
简化版单播委托实现
templateclass Delegate { public: using FuncType = std::function ; Delegate() = default; Delegate(FuncType f) : func(f) {} templateDelegate(T* obj, F f) { func = std::bind(f, obj); } void operator()() const { if (func) func(); } explicit operator bool() const { return static_cast (func); } Delegate& operator=(const FuncType& f) { func = f; return *this; } private: FuncType func; };
这样可以用更直观的方式创建和调用委托:
Delegated(&h, &Handler::OnEvent); d(); // 调用 现代C++建议:优先使用 std::function + Lambda
对于大多数项目,不需要从头造轮子。直接使用 std::function 配合 lambda 更简洁、高效,且易于维护。
优势:
- 兼容所有可调用类型。
- 支持捕获上下文(lambda)。
- 与STL容器良好集成。
- 编译期优化充分。
常见用途:
基本上就这些。不复杂但容易忽略的是生命周期管理——确保被绑定的对象在回调触发时仍然有效。











