Function Pointer及其应用

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);
}

Function Pointer及其应用

上一篇:日记——大学第14天


下一篇:html符号与正则表达式