在上篇文章总结了指针的一些用法,本节则总结了指向函数的一种指针-函数指针的概念与用法。前一段时间做项目的时候遇到了函数指针以及回调,因为之前一直在使用java语言开发,所以对此感觉很纠结。虽然对函数指针有了解,对这个函数指针与回调的使用非常疑惑,于是再次翻阅了c++ primer以及查阅了回调函数的相关含义。下面是一些总结:
1.函数指针概念
在c++ primer中对函数指针的一些解释:
①函数与数组等数据项一样存在地址。函数地址是存储其机器语言代码的内存的开始地址。
②与数组首地址一样,函数的函数名表示该函数的入口地址,即函数地址。
③声明一个函数指针必须指定指针指向的函数类型。函数类型则由函数的返回类型以及函数的特征表(参数列表)表示。声明的格式是:
type (*ptrName) (type,type);
例如:
int (*ptrF)(int,int);
2.函数指针的使用
根据上述定义,可以知道只要符合函数类型的函数均可以赋值给相应的函数指针。从这个角度看,一个函数指针可以表示多个函数,这也是一种多态的体现。函数指针的使用如下:
typedef int (*Pfunc)(int,int);//定义一个类型为函数指针
int (*r_Pfunc(char op))(int,int);//声明一个返回函数指针的函数,返回函数指针类型为int(int,int)*,函数为s_Pfunc(char op)
int Pfunc1(int a,int b);
int Pfunc2(int a,int b);
int Pfunc3(int a,int b);
Pfunc returnPFunc();
int main(){
using namespace std;
Pfunc pfunc;
pfunc=Pfunc1;
pfunc(1,2);
pfunc=Pfunc2;
pfunc(3,4);
pfunc = returnPFunc();
pfunc(5,6);
system("pause");
return 0;
}
int Pfunc1(int a,int b){
cout<<"Pfunc1打印值a="<<a;
cout<<";Pfunc1打印值b="<<b<<"\n";
return 0;
}
int Pfunc2(int a,int b){
cout<<"Pfunc2打印值a="<<a;
cout<<";Pfunc2打印值b="<<b<<"\n";
return 0;
}
int Pfunc3(int a,int b){
cout<<"Pfunc3打印值a="<<a;
cout<<";Pfunc3打印值b="<<b<<"\n";
return 0;
}
Pfunc returnPFunc(){
return Pfunc3;
}
运行结果如下:
根据以上结果可以看出:只要函数指针与某函数的函数类型符合,均可将函数地址赋值给函数指针,进而直接使用函数指针。图示三个函数分别赋值给函数指针,而后调用该函数指针,得出对应函数的执行结果,体现出函数指针的多态性。
在上述实例中,可以看到还有一个关键字的用法:typedef。在这里主要总结了三种用法:
①如上使用typedef定义函数指针复杂的结果为一个函数类型,程序编写可直接使用该函数类型。
②使用typedefy定义结构体。如程序所示
typedef struct s_Strct{
int s1;
double s2;
char s3;
} typeStruct,*ptypeStruct; <pre name="code" class="cpp">//声明一个结构体,对结构体进行自定义类型,其中typeStruct和s_Strct都可以作为类型使用,ptypeStruct是指向该结构体的指针,也可以作为类型使用
typeStruct s_Test={1,1.2,'#'}; //使用typedefine定义的struct的类型 ptypeStruct s_p=new typeStruct; //使用typedefine定义的struct类型指针s_Strct s_ss = { 0,1.1,'&'}; //使用struct名称类型
③使用typedef定义基本类型。具体如下:
//给int类型重新定义一个新类型,以后的int均可被word替换使用
typedef int word;
word w_Value = 30;
3.回调概念与使用
回调函数在c++中应该是是一种函数指针的经典使用方法。在一开是接触回调是总是理解不了,现在回想起来当时的疑问主要有:
- 什么是回调函数?
- 回调函数的“回”字体现在哪?
- 什么时候应该使用回调函数?
本节就从这三个问题着手总结回调函数。
首先什么是回调函数?
答:回调函数是通过函数参数传递到其他代码(函数)的某一块代码的引用(函数地址),这使得底层的代码可以调用高层的子程序。如图所示:
回调函数的回体现在哪里?
答:如上图,回就体现在一开始是底层的testB方法调用高层的testA方法,结果testA方法反过来调用了底层程序中的变量,并回传给高层程序中的变量。所以说回就体现在callback函数调用了调用callback所在层次的变量这一环节上。
那么,根据上面的图示解释,应该已经有了一点概念,现在我们直接将上面的图示用程序运行一下:首先建立两个类:
AB.h文件:
#pragma once
#include <iostream>
using namespace std;
typedef void ( *CallBack)(int,int);
class A
{
public:
A();
~A(void);
static void testA(int a,int b);
void showaValue();
static int aValue;
};
class B
{
private:
int bValue;
int cValue;
public:
B(int bvalue,int cvalue);
~B(void);
void testB(CallBack callback);
};
AB.cpp文件:
#include "AB.h"
int A::aValue =0;
A::A()
{
}
A::~A(void)
{
}
void A::testA(int a,int b)
{
cout<<"执行高层回调函数..."<<"\n";
A::aValue += a+b;
}
void A::showaValue(){
cout<<"A.aValue的值="<<A::aValue<<"\n\n";
}
B::B(int bvalue,int cvalue)
{
bValue = bvalue;
cValue = cvalue;
}
B::~B(void)
{
}
void B::testB(CallBack callback){
cout<<"执行底层方法..."<<"\n";
callback(bValue,cValue);
}
主程序:
<p> </p><pre name="code" class="cpp">int main()
{
A* aclass = new A;
B* bclass = new B(1,2);
aclass->showaValue();
for (int i=0;i<3;i++)
{
cout<<"第"<<i+1<<"次执行底层"<<"\n";
bclass->testB(A::testA);
aclass->showaValue();
}
system("pause");
return 0;
}
运行结果如下:
由此,可以看出底层程序运行三次,调用了三次回调函数,回调函数又反过来调用了三次底层的变量。
什么时候应该使用回调函数?
答:以下是个人实际使用的总结:
①当高层某程序的运行取决于底层程序判断时可用。
②在两个层次之间传递值时可用,这个与观察者模式有点相似,高层(观察者)注册到底层(被观察者),底层(被观察者)根据运行逻辑通知或传递给高层(观察者)消息。所以可参考这个模式使用。
③根据函数指针的多态性,回调也可以实现多态。