如何在C++11类构造函数/函数中传入模板函数替代C风格指针
替代C风格void*函数指针的C++11解决方案
既然你的平台支持C++11核心语言但没有标准库,那完全可以抛弃不安全的void*函数指针方案,用类型安全的方式实现自定义函数传入类的需求。下面给你两个实用的方案,都不需要依赖标准库:
方案1:模板类直接接收任意可调用对象
把接收回调的类做成模板类,这样就能直接兼容普通函数、lambda、自定义函数对象,而且编译期就能检查类型匹配,完全避免void*的隐患。
// 纯C++11核心实现,无标准库依赖 template<typename Callback> class CallbackReceiver { private: Callback m_callback; public: // 构造时直接传入回调对象 explicit CallbackReceiver(Callback cb) : m_callback(cb) {} // 调用回调的方法,这里假设你需要传递int和double参数,可根据需求修改 void invoke(int a, double b) { m_callback(a, b); } }; // 示例自定义函数 void myCustomFunction(int x, double y) { // 这里写你的业务逻辑,比如平台相关的输出/处理 } // 示例自定义函数对象 struct MyFunctor { void operator()(int x, double y) { // 自定义逻辑实现 } }; int main() { // 传入普通函数指针 CallbackReceiver<void(*)(int, double)> receiver1(myCustomFunction); receiver1.invoke(10, 3.14); // 传入C++11 lambda(支持捕获变量,这是void*方案做不到的) auto myLambda = [](int a, double b) { // lambda内的逻辑,比如捕获外部变量的话直接写[&]或者[=] }; CallbackReceiver<decltype(myLambda)> receiver2(myLambda); receiver2.invoke(5, 2.71); // 传入自定义函数对象 MyFunctor functorInstance; CallbackReceiver<MyFunctor> receiver3(functorInstance); receiver3.invoke(20, 1.41); return 0; }
方案2:非模板类+抽象基类包装回调
如果你的接收类不能是模板类(比如需要统一的基类接口),可以用抽象基类定义回调接口,再用模板子类包装任意可调用对象,兼顾类型安全和统一接口。
// 抽象回调基类,定义统一的调用接口 class CallbackBase { public: virtual ~CallbackBase() = default; // C++11支持default关键字生成默认析构 virtual void call(int a, double b) = 0; // 可根据需求修改参数 }; // 模板子类,包装任意可调用对象 template<typename Callback> class CallbackWrapper : public CallbackBase { private: Callback m_callback; public: explicit CallbackWrapper(Callback cb) : m_callback(cb) {} void call(int a, double b) override { m_callback(a, b); } }; // 非模板的回调接收类 class CallbackReceiver { private: CallbackBase* m_callback; public: explicit CallbackReceiver(CallbackBase* cb) : m_callback(cb) {} ~CallbackReceiver() { delete m_callback; // 手动管理内存,无标准库只能这么做 } void invoke(int a, double b) { m_callback->call(a, b); } }; // 使用示例 int main() { // 包装普通函数 CallbackReceiver receiver1(new CallbackWrapper<void(*)(int, double)>(myCustomFunction)); receiver1.invoke(10, 3.14); // 包装lambda auto myLambda = [](int a, double b) { /* 逻辑实现 */ }; CallbackReceiver receiver2(new CallbackWrapper<decltype(myLambda)>(myLambda)); receiver2.invoke(5, 2.71); return 0; }
为什么这比C风格方案好?
- 类型安全:编译期就能检查回调的参数、返回值是否匹配,不会出现void*强制转换导致的运行时崩溃。
- 支持更多场景:lambda可以捕获外部变量,这是C风格函数指针+void*完全做不到的,灵活性拉满。
- 无冗余转换:不需要手动把数据塞到void*里再取出来,代码更简洁易维护。
注意:因为没有标准库,内存管理需要手动处理,比如在CallbackReceiver的析构函数中delete包装器对象,如果需要更安全的内存管理,也可以自己实现一个简单的智能指针(基于C++11的移动语义就行)。
内容的提问来源于stack exchange,提问作者Angus Comber
相关产品推荐
相关产品推荐

