1、What Is A Function Pointer?
函数指针是将函数赋值给一个变量的方法,即指向函数的指针
示例
我们首先通过一个实例理解函数指针的工作,正常调用函数的代码:
#include<iostream>
void HelloWorld(){
std::cout<<"Hello World"<<std::endl;
}
int main(){
HelloWorld();
}
使用函数指针后:
#include<iostream>
void HelloWorld(){
std::cout<<"Hello World"<<std::endl;
}
int main(){
void(*function)() = HelloWorld;
function();
}
运行两个代码发现得到的结果相同
解释
接下来步入正题:
声明一个函数指针:<返回值类型> (*<函数指针名称>)(<参数1的类型>,<参数2的类型>......),例如void Helloworld()函数对应的函数指针可以为:void(*function)()
,再例如int add(int a,int b)对应的函数指针可以为:int (*function)(int,int)
当然啦,也可以使用auto关键字:auto function=Helloworld
实际上 void(*function)() = HelloWorld;
等价为 void(*function)() = &HelloWorld;
,只不过前者用到了隐式转换,这里我要给你们说的是函数指针的实质也是一个地址,指向的是函数在二进制文件中开始的指令所在的地址
实际上我们经常使用typedef关键字对函数指针进行声明和定义,例如 typedef void(*function)()=HelloWorld;
函数指针作为一个实质上的地址当然可以作为参数传入函数:CODE HERE
#include<iostream>
#include<vector>
void PrintValue(int value){
std::cout<<"Value:"<<value<<std::endl;
}
void ForEach(const std::vector<int>& values,void(*func)(int)){
for(int value:values)
func(value);
}
int main(){
std::vector<int> values={1,5,4,2,3};
ForEach(values,PrintValue);
}
这里可以扩展一个tip——Lambda(lambda函数是一种初级函数,也是一种匿名函数,用的时候定义,用完即丢弃):
? 上述代码的另一种表达形式如下:CODE HERE
#include<iostream>
#include<vector>
void ForEach(const std::vector<int>& values,void(*func)(int)){
for(int value:values)
func(value);
}
int main(){
std::vector<int> values={1,5,4,2,3};
ForEach(values,[](int value){std::cout<<"Value:"<<value<<std::endl;});
}
? []叫做捕获方式,是定义匿名函数的一种手段,描述如何出入传出参数,后面跟着参数()
这些就是函数指针的基本工作方式和原理,比较简单,但它的应用就显得没那么简单了
2、函数指针的应用一:做一个驱动,提高性能,为程序提供很强的复用性和可扩展性
我们知道计算机的程序是分成一条条指令存储的(例如add,push,pop,cmp等等等),运行程序也是一条条指令运行的。试想一下,用C++写一个InstuctionsParser该如何写:方法显然,对每个指令编写一个函数,根据swich来调用,可是这样直接调用程序性性能以及可扩展性会大打折扣,于是我们可以用表驱动(GOOGLE|BAIDU),另一种方式便是应用函数指针做任务驱动
我们这里是用我课程老师提供的加减法实现作为示例代码,正常实现加减法并调用:
#include <iostream>
using namespace std;
int Add(int a, int b) { return a + b; }
int Minus(int a, int b) { return a - b; }
void main()
{
char c;
int op1, op2;
cin >> c;
while (c != ‘#‘)
{
cin >> op1;
cin >> op2;
switch (c)
{
case ‘+‘: cout << Add(op1, op2) << endl;
break;
case ‘-‘: cout << Minus(op1, op2) << endl;
break;
}
cin >> c;
}
}
//上述是正常结构化编程的所做的事情,实际上代码复用性和可扩展性非常弱
使用函数指针实现:
//下述使用函数指针FunctionPointer提高代码的复用性和可扩展性
enum OPRAND_TYPE { END = -1, ADD, MINUS };
int Add(int a, int b) { return a + b; }
int Minus(int a, int b) { return a - b; }
struct Task
{
int op1;
OPRAND_TYPE op;
int op2;
};
typedef int(*FP)(int, int);
FP op[2] = { Add, Minus };
OPRAND_TYPE getTask(Task &task)
{
char c;
cin >> c;
switch (c)
{
case ‘#‘: task.op = END; break;
case ‘+‘: task.op = ADD;
cin >> task.op1;
cin >> task.op2;
break;
case ‘-‘: task.op = MINUS;
cin >> task.op1;
cin >> task.op2;
break;
}
return task.op;
}
void executeTask(const Task task)
{
op[task.op](task.op1, task.op2);
}
int main()
{
Task task;
while (getTask(task) != END)
executeTask(task);
}
3、函数指针应用二:提供一种泛型的应用
我们写一个冒泡排序如何写呢?
最初的形式:CODE HERE
#include<iostream>
using namespace std;
void MySort(int A[], unsigned int num)
{
for (unsigned i = 1; i < num; i++)
{
for (unsigned j = 0; j < num - i; j++)
if (A[j] > A[j + 1])
{
int tmp = A[j];
A[j] = A[j + 1];
A[j + 1] = tmp;
}
}
}
//上述代码显然不能提供一种泛型的解决方案
实际上这个代码只能处理int型的数据冒泡排序,而要提供一种支持所有形式数据排序代码的实现方式,我们可以用到函数指针:CODE HERE
struct TStudent
{
char name[20];
int age;
};
void MySort(void *base, unsigned width, unsigned num,
int(*compare)(const void *elem1, const void *elem2)){//这里使用函数指针使用void代表是不明确数据类型的参数,即应用了泛型
char *A = (char *)base; //取结构体数组开始的地址
char *tmp = (char *)malloc(width);//可以自行搜索一下结构体在内存中存储的方式,一个结构体对象占width个字节空间,给结构体对象动态分配一个width大小的空间
for (unsigned i = 1; i < num; i++) {
for (unsigned j = 0; j < num - i; j++) {
if (compare(A + j * width, A + (j + 1)*width) > 0){//if语句的条件判断是作为参数传递过来的一个参数
memcpy(tmp, A + j * width, width);
memcpy(A + j * width, A + (j + 1)*width, width);
memcpy(A + (j + 1)*width, tmp, width); //这三个语句是做了一次空间上的交换
}
}
}
free(tmp);
}
int icompare(const void *elem1, const void *elem2)
{
TStudent *p1 = (TStudent *)elem1;
TStudent *p2 = (TStudent *)elem2;
return p1->age - p2->age;
}
int scompare(const void *elem1, const void *elem2)
{
TStudent *p1 = (TStudent *)elem1;
TStudent *p2 = (TStudent *)elem2;
return strcmp(p1->name, p2->name);
}
int main() {
TStudent student[] = { ("Alan",10),("Blan",18) };
int num = sizeof(student) / sizeof(student[0]);
int width = sizeof(student[0]);
MySort(student, width, num, icompare);
MySort(student, width, num, scompare);
}