函数指针
前言
由(* visit)引发的搜索......
看数据结构代码,不知道这个从哪来的,搜索了一番,还是记一下比较好。
一、 函数指针
1.定义
所谓函数指针即定义一个指向函数的指针变量,定义的格式如下:
int (*p)(int x, int y); //注意:这里的括号不能掉,因为括号()的运算优先级比解引用运算符*高
这个函数的类型是有两个整型参数,返回值是个整型。对应的函数指针类型:
int (*) (int a, int b);
对应的函数指针定义:
int (*p)(int x, int y); //参数名可以去掉,并且通常都是去掉的。这样指针p就可以保存函数类型为两个整型参数,返回值是整型的函数地址了。
int (*p)(int, int);
我们一般可以这么使用,通过函数指针调用函数:
int maxValue (int a, int b) {
return a > b ? a : b;
}
int (*p)(int, int) = NULL; //定义一个与maxValue兼容的指针
p = maxValue;
p(20, 45); //通过指针调用
2.用途
函数指针有两个用途:调用函数和做函数的参数。
个人感觉函数指针最大的作用就是作为函数的参数,当函数需要一个函数作为形参,而这个函数形参未定义或者不确定应该输入哪个函数时,函数指针的作用就体现出来了,使用地址传递的方式,利用函数指针作为形参可以很好地解决这个问题,这有利于程序的模块化与封装性
两个例子
1.像二叉树遍历中使用的 *visit() 函数指针,我们并不清楚,visit应该是输出、操作、赋值等等哪一个函数,因此使用 *visit函数指针,等实际输入时再确定它是哪一个函数。
template<class T>
void BinTree<T>::Preorder(BinTreeNode<T> *subtree,void (*visit)(BinTreeNode<T> *s)){
if(subtree!=NULL){
visit(subtree);
Preorder(subtree->leftChild,visit);
Preorder(subtree->rightChild,visit);
}
}
void printnode(BinTreeNode<char> *tnode){
cout<<tnode->data<<' ';
}
int main(){
BinTree<char> t;
t.buildtree();
t.Levelorder(printnode);
t.Inorder(printnode);
return 0;
}
2 借助于函数指针作为参数实现“动态排序”
首先我们应该理解动态这个词,我的理解就是不同时刻,不同场景,发生不同的事,这就是动态。动态排序就是根据不同的排序指标进行排序,不用书写很多重复性的代码,话不多说,直接上案例。
需求: 有30个学生需要排序
按成绩排
按年龄排
…
这种无法预测的需求变更,就是我们上文说的动态场景,那么解决方案就是函数回调:
//定义一个结构体
typedef struct student
{
char name[20];
int age;
float score;
}Student;
//比较两个学生的年龄
BOOL compareByAge(Student stu1, Student stu2)
{
return stu1.age > stu2.age ? YES : NO;
}
//比较两个学生的成绩
BOOL compareByScore(Student stu1, Student stu2)
{
return stu1.score > stu2.score ? YES : NO;
}
void sortStudents(Student *array, int n, BOOL(*p)(Student, Student))
{
Student temp;
int flag = 0;
for (int i = 0; i < n - 1 && flag == 0; i++)
{
flag = 1;
for (int j = 0; j < n - i - 1; j++)
{
if (p(array[j], array[j + 1]))
{
temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
flag = 0;
}
}
}
}
int main() {
Student stu1 = {"小明", 19, 98};
Student stu2 = {"小红", 20, 78};
Student stu3 = {"小白", 21, 88};
Student stuArray[3] = {stu1, stu2, stu3};
sortStudents(stuArray, 3, compareByAge);//按年龄排
sortStudents(stuArray, 3, compareByScore);//按成绩排
return 0;
}
不难看出,这样使用的便捷性(有点像模板的作用)
--------------------一个小总结--------------------
//定义一个相同类型的函数指针
BOOL(*p)(Student, Student)
//写出两个会使用到的不同函数
BOOL compareByAge(Student stu1, Student stu2)
BOOL compareByScore(Student stu1, Student stu2)
//在另一个函数中调用
void sortStudents(Student *array, int n, BOOL(*p)(Student, Student)) {
....
if (p(array[j], array[j + 1]))
{
....
}
....
}
//在主函数里确定真正调用的函数
int main() {
....
sortStudents(stuArray, 3, compareByAge);//按年龄排
sortStudents(stuArray, 3, compareByScore);//按成绩排
....
}
3.注意事项
我们可以将printnode函数定义为模板函数
template<class T> void printnode(BinTreeNode<T> *tnode){
cout<<tnode->data<<' ';
}
但如果在主函数里定义visit的函数指针模板, 直接如下所示
template<class T> void (*visit)(BinTreeNode<T> *tnode);
c++会报错,(原因我也不太清楚)
解决方法: 把函数指针封装在一个模板结构或模板类里,像在BinTree类中,我们定义一个模板函数指针就可以通过
template <class T>
struct Type {
void (*visit)(BinTreeNode<T> *tnode);
};
或是放到一个模板函数内,让函数指针作为一个形参,在函数内再调用指针指向的函数,但这样也就没有特意定义它的必要,他也就像二叉树遍历函数中的用法一样,作为函数形参,不需要特别定义。
template <class T> void Func( void (*visit)(BinTreeNode<T> *tnode),BinTreeNode<T> *tnode) {
printnode(BinTreeNode<T> *tnode);
}
二、指针函数
1.定义
指针函数:指的是函数的返回值是一个指针,比如我的函数返回的是一个指向整数int的指针,定义格式如下:
int *p(int a,int b); //注意这里的*与P之间是没有括号的,所以含义是函数p(int,int)会返回一个(int *)指针