一,函数指针
1.函数指针的概念
与数组类似,函数在内存中也有地址,函数在内存中的地址是其机器语言代码的开始位置,而函数指针则存储函数的内存地址作为变量。函数指针可以被当作一个值赋给另一个变量,也可以作为实参传递给其他函数,或者作为其他函数的返回结果。通过传递不同的函数给函数指针,可以让一个函数在不同的时间分别调用不同实现的其他函数,这些函数都有一个共同特点,那就是它们的返回值类型还有参数的类型和个数必须相同,这个类似于int类型的指针只能指向包含int值的位置。
函数指针的定义方式:
return_type (*function_pointer_name)(list_of_parameter_types);
注意,函数指针名称外面一定要有圆括号,不然就成了定义一个返回值类型为int指针的函数,样例:
int (*fun_ptr)(float*, int); //返回值类型为int的函数指针fun_ptr
int *fun(float*, int); //返回值类型为int*的函数fun
2.函数指针的初始化
方式一,使用nullptr或函数名称来初始化函数指针。
long get_max(const long* array, size_t size); //获得数组的最大值函数
long (*fun_ptr)(const long*, size_t){get_max}; //用函数名初始化的函数指针
方式二,使用auto关键字初始化函数指针。
auto fun_ptr = get_max;
auto* fun_ptr = get_max;
//以上两种方式的初始化效果是一样的,使用auto*来初始化
//可以强调fun_ptr是一个指针变量,使代码可读性更强
//也可以采用地址运算符&来显式获取函数地址
auto* fun_ptr = &get_max;
auto fun_ptr = &get_max;
方式二的初始化操作比较简单,一旦初始化完成,fun_ptr指向的函数的参数列表和返回值类型将是固定不变的。
3.函数指针调用函数
方式一,使用"(*function_pointer_name)"的方式调用,此方式向代码阅读者强调了使用的是函数指针。
long data[]{ 23, 19, 4, 50 };
long max_data = (*fun_ptr)(data, std::size(data));
方式二,和函数调用的方式类似,直接利用函数指针的名称来调用。
long data[]{ 23, 19, 4, 50 };
long max_data = fun_ptr(data, std::size(data));
完整C++代码实现:
#include <iostream>
using namespace std;
long get_max(const long* array, size_t size);
int main()
{
auto* fun_ptr = &get_max;
//auto fun_ptr = &get_max;
//long (*fun_ptr)(const long*, size_t){get_max};
long data[]{ 23, 19, 4, 50 };
//long max_data = fun_ptr(data, std::size(data));
long max_data = (*fun_ptr)(data, std::size(data));
std::cout << "The max data is: " << max_data << std::endl;
return 0;
}
long get_max(const long* array, size_t size)
{
long max = array[0];
int i = 0;
for (i = 0; i < size; i++)
{
if (max < array[i])
{
max = array[i];
}
}
return max;
}
运行结果:
The max data is: 50
4.函数指针的类型别名
基于using关键字,我们可以给函数指针声明一个类型别名。
类型别名使得函数指针被用作函数参数或者对象的成员变量时的代码量更少。
具体操作如下:
原函数指针的定义:
long (*fun_ptr)(const long*, size_t);
step.01: 去掉函数指针名称,只保留类型关键字。
long (*)(const long*, size_t);
step.02: 基于using关键字的现代语法,左侧是自定义的名称,即类型别名,右侧是函数指针的返回值类型和传参列表。
using Array_max=long (*)(const long*, size_t);
step.03: 用类型别名定义函数指针变量
Array_max fun_ptr;
完整C++代码实现:
#include <iostream>
using namespace std;
using Array_max = long (*)(const long*, size_t);
long get_max(const long* array, size_t size);
int main()
{
Array_max fun_ptr = &get_max;
long data[]{ 23, 19, 4, 50 };
long max_data = fun_ptr(data, std::size(data));
std::cout << "The max data is: " << max_data << std::endl;
return 0;
}
long get_max(const long* array, size_t size)
{
long max = array[0];
int i = 0;
for (i = 0; i < size; i++)
{
if (max < array[i])
{
max = array[i];
}
}
return max;
}
运行结果:
The max data is: 50
同理,也可以基于typedef关键字来声明函数指针的类型别名。
用法如下:
typedef long (*fun_ptr_type)(const long*, size_t);
long get_max(const long* array, size_t size);
//用类型别名来初始化函数指针
fun_ptr_type fun_ptr = &get_max;
完整C++代码实现:
#include <iostream>
using namespace std;
typedef long (*fun_ptr_type)(const long*, size_t);
long get_max(const long* array, size_t size);
int main()
{
fun_ptr_type fun_ptr = &get_max;
long data[]{ 23, 19, 4, 50 };
long max_data = fun_ptr(data, std::size(data));
std::cout << "The max data is: " << max_data << std::endl;
return 0;
}
long get_max(const long* array, size_t size)
{
long max = array[0];
int i = 0;
for (i = 0; i < size; i++)
{
if (max < array[i])
{
max = array[i];
}
}
return max;
}
运行结果:
The max data is: 50
二,回调函数
1.回调函数的概念
回调函数是作为参数传递给另一个函数的函数。
学习回调函数的时候,不能被回调(callback)这个字眼给吓到。
通俗地描述:
这里有三个函数:函数A,函数B,函数C。
回调过程:函数C调用函数B的时候,函数B有个形参是函数指针,函数C将函数A的地址作为参数传给了函数B
主函数:函数C
中间函数:函数B
底层函数:函数A
用伪代码表示:
function A{
...
}
function C{
auto* fun_ptr = &A; //函数指针初始化
auto res = B(*fun_ptr); //调用函数B,并将函数A的地址作为参数传入
}
用C语言来简易实现:
#include<stdio.h>
void A()
{
printf("I am function A\n");
}
void B(void (*ptr)())
{
(*ptr) (); // callback to A
}
int main()
{
void (*ptr)() = &A;
B(ptr);
return 0;
}
运行结果:
I am function A
2.回调函数的代码样例
完整C++代码实现:
#include <map>
#include <iostream>
typedef void (*Callback)();
std::map<int, Callback> callback_map;
void RegisterCallback(int event, Callback func)
{
callback_map[event] = func;
}
bool finished = false;
int GetNextEvent()
{
static int i = 0;
++i;
if (i == 5)
finished = true;
return i;
}
void EventProcessor()
{
while (!finished)
{
int event;
event = GetNextEvent();
std::map<int, Callback>::const_iterator it = callback_map.find(event);
if (it != callback_map.end())
{
auto func_addr = it->second;
Callback func = *func_addr;
if (func)
{
(*func)();
}
else
{
std::cout << "No callback found\n";
}
}
}
}
void Cat()
{
std::cout << "Cat\n";
}
void Dog()
{
std::cout << "Dog\n";
}
void Bird()
{
std::cout << "Bird\n";
}
int main()
{
RegisterCallback(1, Cat);
RegisterCallback(2, Dog);
RegisterCallback(3, Cat);
RegisterCallback(4, Bird);
RegisterCallback(5, Cat);
EventProcessor();
return 0;
}
运行结果:
Cat
Dog
Cat
Bird
Cat
3.使用回调函数的原因
回调函数在两个独立的类或函数之间建立了一种通信渠道,可以通过给主函数传递不同的参数来随时让中间函数调用不同的底层函数。回调函数在事件驱动机制和通信场景下使用起来很方便。
三,参考阅读
《Beginning C++17, 5th Edition》
《C++ Primer Plus, 6th Edition》
https://www.geeksforgeeks.org/callbacks-in-c
https://gist.github.com/DaxDeveloper/9915314
https://en.wikipedia.org/wiki/Callback_(computer_programming)