函数指针与std::function
需要预先了解:
1. 函数指针
1.1 函数的地址
函数的机器指令存储在内存的.text段里,每个函数对应.text段中的一段连续的机器指令序列,其起始地址(即函数第一条指令所在的内存地址)就是这个函数的地址。
1.2 函数指针
函数指针是指向函数的指针类型,它存储函数的入口地址,通过这个地址可以间接调用对应的函数。
其声明语法为返回值类型 (*函数指针名)(参数列表):
int add(int a, int b);
int (*p)(int, int); // p 是类型为 int (*)(int, int)的函数的指针
函数名也可以隐式转换为函数的入口地址
因此有以下两种等价的写法:
int (*p)(int, int) = add;
int (*p)(int, int) = &add;
调用函数指针也有等价的两种写法:
std::cout << p(1, 2) << std::endl;
std::cout << (*p)(1, 2) << std::endl;
2. std::function
std::function定义在头文件<functional>,是通用可调用对象的包装器,可以统一存储、传递、调用任何可调用类型,包括:
- 普通的全局函数
- 类成员函数/静态成员函数
- Lambda表达式
- 函数指针
- 重载
operator()的仿函数
其语法模板为:
template<class R, class... Args>
class function<R(Args...)>;
2.1 具体操作
假定有如下定义:
#include <functional>
#include <iostream>
void foo()
{
std::cout << "foo called" << std::endl;
}
int add(int x, int y)
{
return x + y;
}
1. 构造函数:可以从函数指针、函数对象、Lambda表达式等创建std::function对象
// 创建一个 std::function 对象,指向 foo
std::function<void()> f1 = foo;
f1(); // 调用 f1 指向的对象,即 foo
// 创建一个 std::function 对象,指向 add
std::function<int(int, int)> f2 = add;
std::cout << f2(1, 2) << std::endl;
// 创建一个 std::function 对象,指向一个 Lambda 表达式
std::function(void()) f3 = [] { std::cout <<"this is Lambda!" << std::endl; }
f3();
2. 空检查:用bool operator() const或bool operator bool() const判断std::function对象是否为空
std::function<void()> func = nullptr;
if(func)
{
std::cout << "func 不为空" << std::endl;
}
else
{
std::cout << "func 是空的" << std::endl;
}
3. 封装仿函数:仿函数即重载operator ()的类,可以像函数一样对其进行调用
struct A
{
void operator()(double x)
{
std::cout << x << std::endl;
}
};
A A_instance;
std::function<void(double)> func = A_instance;
func(3.14);
3. std::bind
std::bind是参数绑定器,接收一个可调用对象(函数、成员函数、lambda表达式、仿函数、函数指针)以及参数,返回一个新的可调用对象(类型为函数对应的std::functiom)。其定义在头文件<functional>里。
其语法为:
auto new_func = std::bind(可调用对象, 绑定参数列表);
3.1 占位符 std::placeholders
std::placeholders::_1、std::placeholders::_2、……、std::placeholders::_N依次表示新函数调用时候传入的第1个实参、第2个实参、……、第N个实参。
3.2 用途
1. 用std::bind固定部分参数:
int sum(int x, int y, int z) { return x + y + z; }
std::function<int(int, int)> f =
std::bind(sum, 10, std::placeholders::_1, std::placeholders::_2);
std::cout << f(1, 2) << std::endl;
2. 用std::bind打乱参数顺序:
int sub(int a, int b) { return a - b; }
auto f = std::bind(sub, std::placeholders::_2, std::placeholders::_1);
f(10, 3);
3. 用std::bind固定类的成员函数:类的成员函数隐含第一个参数是this指针的含义,因此需按照std::bind(&类::成员函数,对象的地址,其余参数/占位符)的方式进行传入:
struct student
{
void print() { std::cout << "I'm a student" << std::endl; }
}
Student s;
auto func = std::bind(&Student::print, &s);
注:
静态成员函数没有this指针,用法和普通全局函数一致,不需要传对象。


