前言
前面我们学习了 lambda 表达式,lambda 本质是编译器自动生成的仿函数,属于可调用对象。C++ 里面可调用对象种类很多:普通函数、函数指针、仿函数、lambda、类成员函数。它们类型各不相同,直接统一管理很麻烦。
C++11 在<functional>头文件提供了包装器:std::function和std::bind,用来统一封装、适配各类可调用对象。
function 可调用对象包装器
函数指针、仿函数、 lambda 等可调用对象的类型各不相同, std::function 的优势就是统 一类型,对他们都可以进行包装,这样在很多地方就方便声明可调用对象的类型
语法:
function<返回值类型(参数类型列表)> 变量名=可调用函数
对于一些可以调用的普通函数,仿函数lambda
对于成员函数
静态成员函数
- 静态成员函数没有 this 指针,和普通全局函数一样。
- 获取成员函数地址语法:
&类名::函数名,&不能省略(成员函数取地址要求)。 - 签名就是
int(int,int),直接包装,调用只传两个参数。
普通成员函数
普通成员函数内部可以访问对象的成员变量,而成员变量存储在实例对象里面。 C++ 编译器给普通成员函数增加了一个隐藏参数this(对象指针),用来标记当前操作的是哪一个对象。 使用std::function包装成员函数时,我们要手动把这个隐藏的 this 参数写在 function 签名的第一个位置,调用的时候传入对象地址。 没有对象 /this 指针,成员函数找不到成员变量,无法运行。
| 特性 | 普通成员函数 | 静态成员函数 |
|---|---|---|
隐含this指针 | ✅ 有 | ❌ 无 |
| 访问非静态成员 | ✅ 可以 | ❌ 不可以 |
| 访问静态成员 | ✅ 可以 | ✅ 可以 |
| 调用方式 | 需通过对象 | 类名::函数()或对象调用 |
| 是否属于对象 | 属于对象 | 属于类 |
能否为virtual | ✅ 可以 | ❌ 不可以 |
能否为const | ✅ 可以 | ❌ 不可以(无 this) |
bind 绑定器
作用
std::bind可以接收一个可调用对象,绑定固定参数,或者调整参数顺序,生成一个全新的可调用对象。 配合占位符placeholders::_1、placeholders::_2来代表新函数的第 1、第 2 个参数。
调用bind的一般形式:
auto newCallable = bind(callable,arg_list); 其中 newCallable本身是一个可调用对象,arg_list是一个逗号分隔的参数列表,对应给定的callable的 参数。当我们调用newCallable时,newCallable会调用callable,并传给它arg_list中的参数。
A. 调整参数顺序 (Parameter Reordering)
代码中的sub2演示了这一点。
原函数:
Sub(a, b)计算(a - b) * 10。代码:
auto sub2 = bind(Sub, _2, _1);_2代表新函数的第 2 个参数。_1代表新函数的第 1 个参数。
调用:
sub2(10, 20)。此时
_1= 10,_2= 20。传递给原函数
Sub的参数变成了Sub(20, 10)。结果:
(20 - 10) * 10 = 100。作用:交换了传入参数的顺序。
B. 绑定固定参数 (Binding Fixed Arguments)
代码中的sub3演示了这一点(也叫“部分应用” Partial Application)。
代码:
auto sub3 = bind(Sub, 100, _1);第一个参数被固定为
100。第二个参数保留占位符
_1,表示需要用户在调用时传入。
调用:
sub3(20)。此处
_1= 20。实际调用的是
Sub(100, 20)。结果:
(100 - 20) * 10 = 800。作用:减少参数数量,预设默认值。
小练习
计算复利