题外话:
和函数指针类似的有个指针函数,很简单,顺便提一句。
指针函数就是一个函数,但是它的返回值是指针类型
c
int* Add(int a, int b)
{
int* c = new int;
*c = a + b;
return c;
}
归根结底,指针函数还是一个函数,和普通函数没有什么区别。
函数指针
函数指针是指向函数的指针变量。在C/C++语言中,函数名实际上就是一个函数指针,它存储了函数的入口地址。函数指针的声明和使用方式类似于普通指针。同时函数指针可以调用指向的函数,使函数的调用更加灵活。
函数指针的主要作用是实现回调函数,在程序运行时动态地将某个函数的执行权传递给另一个函数。比如,在处理数据时,可能需要用到很多不同的算法,这时候可以通过函数指针来动态地选择不同的算法进行处理。函数指针还常用于实现动态绑定和动态分发。
函数地址
函数也是有地址的,可以用函数名表示地址或者在函数名前加&表示。
c
#include <iostream>
#include <iomanip>
int Add(int a, int b)
{
return a + b;
}
int main()
{
std::cout << std::hex << Add << std::endl;
std::cout << std::hex << &Add << std::endl;
system("pause");
return 0;
}
输出:
c
00007FF71ADD11FE
00007FF71ADD11FE
可以看到,两种方式的十六进制输出是相同的。
定义函数指针
函数指针的声明如下:
c
返回值类型 (*指针变量名)(参数列表);
其中,指针变量名就是函数指针的变量名。
例如:
c
// 定义函数指针变量pf
int (*pf)(int a, int b);
有的时候函数的参数列表和返回值比较复杂,每次定义这样的函数指针都要重写一遍比较烦琐。因此可以用类型定义运算符"typedef"为该函数定义一个简单的类型名。有了这样一个类型名之后,就可以用来定义函数指针变量,而不用重写函数参数列表和返回类型。
例如:
c
typedef int (*FuncPtr)(int a, int b);
c
#include <iostream>
#include <iomanip>
typedef int (*FuncPtr)(int a, int b);
int Add(int a, int b)
{
return a + b;
}
int main()
{
// 方法1
int (*pf)(int a, int b);
pf = Add;
std::cout << pf(2, 3) << std::endl;
// 方法2
int (*pf1)(int a, int b) = Add;
std::cout << pf1(2, 3) << std::endl;
// 方法3
FuncPtr func = Add;
std::cout << func(2, 3) << std::endl;
system("pause");
return 0;
}
这是函数指针的三种写法以及调用,假设我们的Add函数有多个参数,这种时候使用typedef提前声明非常方便。
回调函数
函数指针常被用于回调函数,回调函数是一种在程序运行过程中通过函数指针来实现的函数调用方式。它的基本思想是将一个函数的地址作为参数传递给另一个函数,在合适的时候调用这个函数来完成一定的任务。
使用回调函数的主要目的是实现代码的灵活性和扩展性,将某个需要在特定事件发生时执行的函数的执行权交给另一个函数。通常,回调函数包括两个步骤:首先,程序需要定义一个回调函数,然后,程序中的某个部分会使用这个回调函数,在特定情况下触发执行。
c
#include <iostream>
#include <iomanip>
typedef int (*FuncPtr)(int a, int b);
// 回调函数
int Add(int a, int b)
{
return a + b;
}
// 使用回调函数的函数
void Test(int a, int b, FuncPtr func)
{
std::cout << func(a, b) << std::endl;
}
int main()
{
Test(2, 3, Add);
system("pause");
return 0;
}
回调函数用于异步编程示例
c
#include <iostream>
#include <thread>
#include <functional>
typedef void (*CallBack)();
// 异步函数
void AsyncFunction(CallBack callback) {
std::cout << "异步函数开始执行..." << std::endl;
// 模拟异步操作,等待2秒钟
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "异步函数执行完毕。" << std::endl;
// 调用回调函数
callback();
}
// 回调函数实现
void CallbackFunction() {
std::cout << "回调函数被调用。" << std::endl;
}
int main() {
std::cout << "主函数开始执行。" << std::endl;
// 调用异步函数,并传递回调函数作为参数
AsyncFunction(CallbackFunction);
std::cout << "主函数继续执行。" << std::endl;
// 模拟主线程继续执行其他任务
std::this_thread::sleep_for(std::chrono::seconds(3));
std::cout << "主函数执行完毕。" << std::endl;
system("pause");
return 0;
}
输出结果:
c
主函数开始执行。
异步函数开始执行...
// 等待2s
异步函数执行完毕。
回调函数被调用。
主函数继续执行。
// 等待3s
主函数执行完毕。
回调函数通常在以下情况下被广泛使用:
1、异步操作:当需要执行异步操作时,回调函数是一种常见的异步通知方式。例如,网络请求、文件读写、定时器等操作都可以在操作完成后使用回调函数进行通知。
2、事件处理:当需要响应特定的事件时,回调函数是一种常见的事件处理方式。例如,用户输入事件、鼠标点击事件、按键事件等都可以使用回调函数的方式进行处理。
3、定制逻辑:当需要定制特定的逻辑时,回调函数可以允许用户自定义代码片段。例如,图形界面库通常会提供一些回调函数用于用户自定义处理窗口关闭、按钮点击等事件。
4、并发处理:当需要处理多个任务并发执行时,回调函数可以用于处理每个任务的完成通知。例如,线程池中的任务完成后会调用回调函数进行处理。
5、插件扩展:当需要扩展现有功能时,回调函数可以被用作插件系统的一种扩展方式。例如,文本编辑器可以提供一些回调函数接口,供插件开发者自定义功能。