当前位置: 首页 > ai >正文

C++ 中的函数包装:std::bind()、std::function<>、函数指针与 Lambda

函数包装(Function Wrapping)是一种编程技术,指的是将一个函数(或可调用对象)封装在另一个函数或对象中,以扩展其功能、修改其行为或提供额外的上下文。函数包装的核心思想是通过一个“包装器”来间接调用原始函数,同时可以在调用前后执行额外的逻辑。

函数指针

函数指针是 C++ 中最基础的函数包装方式。可以将函数的地址存储在指针中,并通过该指针调用函数。

#include <iostream>void printHello() {std::cout << "Hello, World!" << std::endl;
}
void printSum(int a, int b) {std::cout << "Sum: " << a + b << std::endl;
}int main() {void (*funcPtr)() = &printHello;funcPtr();  // 输出: Hello, World!void (*funcPtr2)(int, int) = &printSum;funcPtr2(10, 20); // 输出: Sum: 30return 0;
}

void (*funcPtr2)(int, int)

  • funcPtr2:函数指针起的名字,就像变量名一样。
  • *:表明funcPtr2是一个指针,是指向函数的指针,而不是指向简单数据类型的普通指针。
  • void:指出该指针所指向的函数返回值类型为void
  • (int, int):函数的参数列表,该函数接受两个int类型的参数。

优势

  • 简单直接:函数指针的概念简单,易于理解和使用。
  • 性能高:函数指针直接指向函数地址,调用开销小。

缺点

  • 类型不安全:函数指针的类型检查较弱,容易出错。
  • 灵活性差:无法直接绑定成员函数或带有状态的函数。

std::function<>

std::function<> 是 C++11 引入的一个通用函数包装器,它可以存储任何可调用对象(如函数、Lambda 表达式、成员函数等)。

需要头文件:#include <functional>

#include <iostream>
#include <functional>void printHello() {std::cout << "Hello, World!" << std::endl;
}void printSum(int a, int b) {std::cout << "Sum: " << a + b << std::endl;
}int main() {std::function<void()> func = printHello;func();  // 输出: Hello, World!std::function<void(int, int)> func2 = printSum;func2(10, 20);  // 输出: Sum: 30// 接收lambda表达式类型std::function<int(int, int)> func3 = [](int a, int b) {return a * b;};func3(10, 20);  // 输出:30return 0;
}

std::function<void(int, int)> func2 = printSum:

std::function<...>:是一个模板类,用于封装可调用对象。模板参数描述了被封装对象的签名。

void(int, int):模板参数,描述了被封装对象的签名,这里是接受了两个int类型的参数返回类型为void

在代码中,也可以使用using 关键字与 std::function<> 结合使用,用于定义类型别名,从而简化代码并提高可读性。

#include <iostream>
#include <functional>// 使用 using 定义 std::function 类型别名
using IntFunction = std::function<int(int, int)>;// 普通函数
int add(int a, int b) {return a + b;
}int main() {// 使用别名声明 std::function 对象IntFunction func = add;// 调用std::cout << "Result: " << func(2, 3) << std::endl; // 输出 5return 0;
}

优势

  • 通用性强:可以存储任何可调用对象,包括函数、Lambda 表达式、成员函数等。
  • 类型安全std::function<> 提供了类型检查,减少了出错的可能性。

缺点

  • 性能开销:相比于函数指针,std::function<> 有一定的性能开销,尤其是在存储复杂对象时。

std::bind()

std::bind() 是 C++11 引入的一个函数适配器,它可以将函数与参数绑定在一起,生成一个新的可调用对象。

需要头文件:#include <functional>

#include <iostream>
#include <functional>void printSum(int a, int b) {std::cout << "Sum: " << a + b << std::endl;
}int main() {auto boundFunc = std::bind(printSum, 10, std::placeholders::_1);boundFunc(20);  // 输出: Sum: 30return 0;
}

优势

  • 参数绑定:可以将部分参数预先绑定,生成一个新的可调用对象。
  • 灵活性高:可以绑定成员函数、普通函数等。

缺点

  • 可读性差std::bind() 的语法较为复杂,可读性较差。
  • 性能开销:与 std::function<> 类似,std::bind() 也有一定的性能开销。

Lambda 表达式

Lambda 表达式是 C++11 引入的一种匿名函数,它可以在代码中直接定义并使用。

#include <iostream>int main() {auto lambda = [](int a, int b) {std::cout << "Sum: " << a + b << std::endl;};lambda(10, 20);  // 输出: Sum: 30return 0;
}

C++11特性详解-CSDN博客 可看lambda表达式具体使用方法。

优势

  • 简洁:Lambda 表达式可以在代码中直接定义,无需单独声明函数。
  • 捕获外部变量:Lambda 表达式可以捕获外部变量,使得函数更加灵活。

缺点

  • 可读性差:复杂的 Lambda 表达式可能会降低代码的可读性。
  • 性能开销:捕获外部变量时,Lambda 表达式可能会引入额外的性能开销。

其他函数包装方式

除了上述几种方式,C++ 还提供了其他一些函数包装技术,如函数对象(Functor)和成员函数指针。

函数对象(Functor)

函数对象是一个重载了 operator() 的类对象,它可以像函数一样被调用。

#include <iostream>struct Sum {void operator()(int a, int b) const {std::cout << "Sum: " << a + b << std::endl;}
};int main() {Sum sum;sum(10, 20);  // 输出: Sum: 30return 0;
}

成员函数指针

成员函数指针可以指向类的成员函数,并通过对象或指针调用。

#include <iostream>class MyClass {
public:void printHello() {std::cout << "Hello, World!" << std::endl;}void printSum(int a, int b) {std::cout << "Sum: " << a + b << std::endl;}
};int main() {MyClass obj;void (MyClass::*funcPtr)() = &MyClass::printHello;(obj.*funcPtr)();  // 输出: Hello, World!void (MyClass::*funcPtr2)(int, int) = &MyClass::printSum;(obj.*funcPtr2)(10, 20);  // 输出: Sum: 30return 0;
}

总结:

  • 函数指针:适用于简单的函数调用场景,性能要求高。
  • std::function<>:适用于需要存储多种可调用对象的场景,类型安全。
  • std::bind():适用于需要预先绑定参数的场景,灵活性高。
  • Lambda 表达式:适用于需要简洁、灵活的函数定义场景,尤其是需要捕获外部变量的情况。
http://www.xdnf.cn/news/9156.html

相关文章:

  • ComfyUI 文生图,绘图要求中 正向提示词、负向提示词 有什么区别,webp又是什么格式 comfyui 那么喜欢它
  • 冯 • 诺依曼体系结构
  • 在 Unity 中,Start 方法直接设置 RectTransform 的位置,时出现问题,与预计位置不匹配。
  • Vue 3.0 中provide常见使用场景
  • 一个开源的多播放源自动采集在线影视网站
  • GitCode镜像仓库批量下载开发实录
  • 【Redis】过期键删除策略,LRU和LFU在redis中的实现,缓存与数据库双写一致性问题,go案例
  • 第12次08: 省市县区三级联动收货地址
  • 沉石鱼惊旋
  • CVE-2021-44228源码分析与漏洞复现
  • Redis学习打卡-Day7-高可用(下)
  • 学习心得(19)如何验证Form表单里的数据是对的?
  • 并发基础|进程与线程
  • 使用NSIS 和 VNISEdit 打包 electron 程序为 exe 向导式安装包
  • 制药行业数字化转型:从挑战到智能化落地的实践路径
  • 嵌入式学习Day28
  • FreeRTOS——事件标志组
  • Java 权威方案:彻底修复 OPTIONS 方法安全漏洞(附企业级案例与测试指南)
  • 今日行情明日机会——20250526
  • 固态硬盘不识别或掉盘如何解决?——以Kingston FURY Renegade G5为例
  • Qwen-Agent的使用示例-天气查询(function calling)
  • 电子电路原理第十七章(线性运算放大器电路的应用)
  • 【登录优化】redis删除旧token
  • AI测试进入智能体时代:AutoGen 、 Coze、CrewAI 谁主沉浮?
  • C++ STL map multimap 查找操作详解
  • 2025-5-26Vue3快速上手
  • Nginx location匹配模式详解
  • 解锁 MCP 中的 JSON-RPC:跨平台通信的奥秘
  • nfs下载镜像报错File lookup fail,TTTTTTTTTTTTTTT,内核 6.11.0降到5.15.0
  • JAVA面试复习知识点