北京 网站建设公司,企业网站建设 深圳,手机网站建设怎么样,广告推销网站一、函数指针简介 函数指针是指指向函数而非指向对象的指针。像其他指针一样#xff0c;函数指针也指向某个特定的类型。函数类型由其返回类型以及形参表确定#xff0c;而与函数名无关。例如#xff1a;
char* (*pf1)(char * p1,char *p2); 这是一个函数指针#xff0c;其…一、函数指针简介 函数指针是指指向函数而非指向对象的指针。像其他指针一样函数指针也指向某个特定的类型。函数类型由其返回类型以及形参表确定而与函数名无关。例如
char* (*pf1)(char * p1,char *p2); 这是一个函数指针其真实词意如果转换一下,似乎更好理解,只是编译器不会这样排版而已
char* (*)(char * p1,char *p2) pf1; 将 pf 声明为指向函数的指针它所指向的函数带有两个 char* 类型的形参和 char*类型的返回值。注意*pf两侧的圆括号是必需的否则就成了返回char**类型的普通函数声明了。参数类型是必须的但参数名不是必须的可以省略。
char* (*pf1)(char*,char*); //char* (*)(char*,char*) pf1; 通常我们在开发中尤其是应用层级开发中较少使用函数指针也不建议使用函数指针。但是由于函数指针其特殊性可以迸发出很多巧妙的代码组织方法尤其是很多底层驱动开发中不少地方都会用到函数指针。 函数指针类型相当地冗长。使用 typedef 为指针类型定义同义词可将函数指针的使用大大简化
typedef char* (*pfunc)(char*, char*); 声明函数指针后需要给予初始化或赋值才能使用
//先定义一个真实函数
char* fun1(char * p1,char *p2)
{return ((0 strcmp(p1,p2))?p1:p2);
};//char* (*pf1)(char*, char*) fun1;//直接初始化
char* (*pf1)(char*, char*);
pf1 fun1; //fun1等同于char* (*)(char*, char*)char p1[]abc;
char p2[]bcd;
printf(%s\n, (*pf1)(p1,p2)); 函数名作为右值时在引用函数名但又没有调用该函数时函数名将被自动解释为指向函数的指针因此采用func或func给函数指针赋值都可以以下四种中可以达成目的。
//func1除了用作函数调用的左操作数以外对 fun1的任何使用都被解释为char* (*)(char*, char*)
char* (*pf1)(char*, char*) fun1;//直接初始化
char* (*pf2)(char*, char*) fun1;//直接初始化
char* (*pf3)(char*, char*);//
char* (*pf4)(char*, char*);//
pf3 func1;
pf4 func1;
typedef char* (*pfunc)(char*, char*);
pfunc pf5 fun1;
pfunc pf6 fun1; 函数指针只能通过同类型的函数或函数指针或 0 值常量表达式进行初始化或赋值。
char* (*pf1)(char*, char*);
pf1 fun1;
//
pfunc pf3 0; //初始化为 0表示该指针不指向任何函数
pf3 pf1;char fun3(char * p1,char *p2)
{return ((0 strcmp(p1,p2))?p1:p2);
};char* fun4(char p1,char p2)
{return ((0 strcmp(p1,p2))?p1:p2);
};pf3 fun3;//eroor,返回类型不一致
pf3 fun4;//eroor参数类型不一致 指向函数的指针可用于调用它所指向的函数支持显式或隐式方式。 char p1[]abc;char p2[]bcd;pfunc pf3 fun1;printf(%s\n,pf3(p1,p2)); //隐式调用printf(%s\n,(*pf3)(p1,p2)); //显式调用
二、函数指针数组 函数指针数组就是将函数指针存储在一个数组里假设定义一个函数指针
void (*FuncPtr)(); 那么FuncPtr就是一个函数指针既然 FuncPtr是一个指针那就可以储存在一个数组里。
void (*FuncPtr[])();
或
void (*FuncPtr[2])(); 定义一个函数指针数组。它是一个数组数组名为 FuncPtr数组内存储了 3 个或未知个数的指向函数的指针。这些指针指向一些返回值类型为void、无参数的函数。当然也可以通过typedef 修饰一下就像定义变量数组一样
typedef void (*FuncPtr)(); FuncPtr pfunc[3]; 同样函数指针数组也要给其赋值指向真实函数地址才能使用
typedef void (*FuncPtr)(); void doSomething1()
{printf(doSomething1\n);
};void doSomething2()
{printf(doSomething2\n);
};void (*fp[2])();
fp[0] doSomething1;
fp[1] doSomething2;
fp[0](); //函数调用FuncPtr pfunc[2] {doSomething1,doSomething2};
pfunc[0](); //函数调用FuncPtr funcPtrArray[2];
funcPtrArray[0] doSomething1; //可以直接用函数名
funcPtrArray[1] doSomething2; //可以用函数名加上取地址符
funcPtrArray[1](); //函数调用 如果函数返回值有差异但又想通过函数指针数组归一化起来在确保安全前提下通过reinterpret_cast进行函数指针类型转换注意c中是没有reinterpret_cast的。
typedef void (*FuncPtr)(); int doSomething3()
{printf(doSomething3\n);return 0;
};FuncPtr funcPtrArray[3];
funcPtrArray[0] doSomething1; //可以直接用函数名
funcPtrArray[1] doSomething2; //可以用函数名加上取地址符
//注意doSomething3直接传递是类型不符合的它对于funcPtrArray是一个错误类型
//c的reinterpret_cast 可以让你迫使编译器以你的方法去处理,
//转换函数指针,C不保证所有的函数指针都被用一样的方法表示在一些情况下这样的转换会产生不正确的结果.
funcPtrArray[2] reinterpret_castFuncPtr(doSomething3);//g编译时生效c语言编译时可仿c的reinterpret_cast创建一个类似的带参宏定义
#define reinterpret_cast(TYPE,EXPR) ((TYPE)(EXPR)) //仿c的reinterpret_cast
//此处省略声明定义
funcPtrArray[2] reinterpret_cast(FuncPtr, doSomething3);//gcc或g编译时都生效
funcPtrArray[2]();
三、指向重载函数的指针 C语言允许使用函数指针指向重载的函数假设在func.cpp中定义了函数func_test
//func.cpp
int func_test(int p1, int p2)
{return (p1p2)?p1:p2;
}; 在test.cpp中通过extern实现重载。
//test.cpp
extern int func_test(int, int);//指向重载函数的指针
int (*pft)(int, int) func_test;
printf(min_val:%d\n, (*pft)(6,7)); 注意指针的类型必须与重载函数的精确匹配。如果没有精确匹配的函数则对该指针的初始化或赋值都将导致编译错误:
//test.cpp
int (*pf2)(int) func_test; //error,invalid parametervoid (*pf3)(int, int);
pf3 func_test; // error, invalid return type
四、函数指针作为类成员 函数指针也是指针既然是指针就可以作为类成员变量来使用。 假设有如下类CallBack其成员变量是一个函数指针CallBackPtr及void*的指针初始化时传递一个函数指针和void变量指针实现初始化通过doCallBack函数具体调用CallBackPtr实现函数回调
//在传递函数指针时建议进行这种异常规格的检查
typedef void (*CallBackPtr)(void *data_);
class CallBack
{public:CallBack(CallBackPtr fPtr, void *data_): func(fPtr), data(data_) {}void doCallBack() const throw();
private:CallBackPtr func; // function to call when// callback is madevoid *data; // data to pass to callback
}; void CallBack::doCallBack() const throw()
{func(data);
}该类使用时定义具体业务实现的真实函数将这些函数指针传递给CallBack类业务实际需要滞后到实现具体业务场景明确时实现其逻辑如下 void callBackFcn1(void *data_)
{printf(callBackFcn1\n);
};void callBackFcn2(void *data_) throw()
{printf(callBackFcn2\n);
};
//回调,将具体实现放置构造传递进去的具体函数指针
void *callBackData;
CallBack c_instance(callBackFcn1,callBackData);
c_instance.doCallBack(); 在传递函数指针时建议进行这种异常规格的检查大家可以想想如下函数指针定义时指向的真实函数定义该如何设计。
typedef void (*CallBackPtr)(void *data_) throw();//如果不提供异常说明该指针就可以指向能够抛出任意类型异常的具有匹配类型的函数
typedef void (*CallBackPtr)(void *data_) throw(runtime_error);//指定异常时只能抛出 runtime_error 类型的异常
typedef void (*CallBackPtr)(void *data_) const throw();//类型定义更严格,源指针的异常说明必须至少与目标指针的一样严格
五、函数指针作为形参 函数指针也是指针既然是指针就可以作为参数传递给函数函数就可以在其内部使用该函数指针从而实现对函数指针指向的函数进行调用实现函数回调。想STL标准了内的排序函数 模板就是通过传递一个数值比较的函数指针来实现的。
//函数指针作为形参
class Aclass
{
public:Aclass(void* data_) : vdata(data_){};void doSomething1(){printf(Aclass doSomething1\n);};void doSomething2(){printf(Aclass doSomething2\n);};void* getData(){return vdata;}
private:void* vdata;
};void myfunc(Aclass* ptr,void* data,bool (*compare)(void const* pd1, void const* pd2))
{if(compare(ptr-getData(),data))ptr-doSomething1();elseptr-doSomething2();
};bool compare_int(void const* pd1, void const* pd2)
{return (*(int*)pd1*(int*)pd2)?false:true;
} 如上述代码myfunc函数参数很复杂将类对象数值及函数指针作为参数传递进去。函数指针通过重定义一下
typedef bool (*Func_compare)(void const* pd1, void const* pd2);void myfunc(Aclass* ptr,void* data,Func_compare compare); 函数调用如下
int aval 10;
int bval 8;
Aclass a_obj(aval);
myfunc(a_obj,bval,compare_int); //
六、 转换表 有以下一种功能要求,这是一个实现操作符计算的分支设计如果有上百个操作符需要实现呢这个switch语句就会很长
switch(oper)
{case ADD:ret add(val1,val2);break;case SUB:ret sub(val1,val2);break;case MUL:ret mul(val1,val2);break;case DIV:ret div(val1,val2);break;......default:ret 0.0;break;
} 通过建立一个函数指针数组作为转换表就可以实现类似switch的代码功能
enum Method{ADD0,SUB,MUL,DIV
};typedef float (*operFunc)(float fa, float fb);operFunc operf[] {add,sub,mul,div}; 其调用如下确保这些操作符函数定义在初始化列表之前初始化列表的函数名顺序取决与程序用来表示每个操作符的整型代码。
//操作符具体实现函数
float add(float fa, float fb)
{return fafb;
};
float sub(float fa, float fb)
{return fa-fb;
};
float mul(float fa, float fb)
{return fa*fb;
};const float abs_min_val 0.0000001;
float div(float fa, float fb)
{if((fbabs_min_val)(fb(-abs_min_val))){return fa/abs_min_val;}return fa/fb;
};
//函数指针数组调用实现类switch功能
float afval 7.8, bfval 8.5;
float ret_fval operf[Method::ADD](afval,bfval);
printf(%0.4f\n,ret_fval);七、类函数指针成员与注册函数 函数指针是指针就可以作为类的成员变量通常用于将具体业务实现剥离在类外定义实现在业务执行是注册到类中从而针对具体业务来调用需要的业务模块业务函数。
//函数指针成员,注册函数
class BClass
{
public:BClass(){my_compare NULL;};typedef bool (*compare)(void const* pd1, void const* pd2);void my_compare_do(void const* cm1, void const* cm2) //具体实现通过调用外部函数{if(NULLmy_compare)printf(please register func first!\n);if(my_compare(cm1,cm2)) //外部函数调用printf(do it A\n);elseprintf(do it B\n);};void register_func(compare cp1) //注册函数{my_compare cp1;};compare my_compare; //函数指针变量
};在具体业务逻辑过程中先给类实例注入调用函数在调用具体函数实现业务逻辑。 //注册函数实现外部函数调用BClass bObject;bObject.register_func(compare_int);bObject.my_compare_do(aval,bval);
八、类函数指针数组 同样地函数指针还可以通过函数指针数组作为类成员列表这些成员的实现细节当然是可以在类内部外部或外部模块来具体实现。例如在很多程序会这样做在程序中的每个类只要声明函数指针数组然后在外部来具体实现业务甚至是交给二次开发者依据具体业务场景来针对性设计功能函数。
//类函数指针数组test(成员列表)
class OperateKey
{
public:OperateKey(){};enum Directions { FORWARD, BACK, UP, DOWN };OperateKey do_it(Directions direct_){ //记需要有效性判断(this-vtbl[direct_])();return *this;};typedef void (*Action)();static Action vtbl[];
};void forward()
{printf(forward\n);
};
void back()
{printf(back\n);
};
void up()
{printf(up\n);
};
void down()
{printf(down\n);
};OperateKey::Action OperateKey::vtbl[] {forward, back, up, down}; 类的函数指针数组成员列表就像平常类实例使用数组变量一样使用即可只是数组变量是作为变量使用而函数指针数组存储的是函数名函数地址作为一个个函数使用。 //类函数指针数组成员列表OperateKey myKeys;myKeys.do_it(OperateKey::FORWARD);myKeys.do_it(OperateKey::DOWN);
九、函数指针测试案例 创建test.cpp和func.cpp文件主要是代码是在test.cpp中实现。 func.cpp用来测试函数指针重载
//
int func_test(int p1, int p2)
{return (p1p2)?p1:p2;
}; test.cpp
#include string.h
#include stdio.h//函数指针test
char* fun1(char * p1,char *p2)
{return ((0 strcmp(p1,p2))?p1:p2);
};char* fun2(char * p1,char *p2)
{return ((0 strcmp(p1,p2))?p2:p1);
};typedef char* (*pfunc)(char*, char*);//函数指针数组test
typedef void (*FuncPtr)(); #define reinterpret_cast(TYPE,EXPR) ((TYPE)(EXPR))void doSomething1()
{printf(doSomething1\n);
};void doSomething2()
{printf(doSomething2\n);
};int doSomething3()
{printf(doSomething3\n);return 0;
};
//
extern int func_test(int, int);
//回调
//在传递函数指针时建议进行这种异常规格的检查
typedef void (*CallBackPtr)(void *data_);
//typedef void (*CallBackPtr)(void *data_) throw();?//如果不提供异常说明该指针就可以指向能够抛出任意类型异常的具有匹配类型的函数
//typedef void (*CallBackPtr)(void *data_) throw(runtime_error);?//指定异常时只能抛出 runtime_error 类型的异常
//typedef void (*CallBackPtr)(void *data_) const throw();?//类型定义更严格,源指针的异常说明必须至少与目标指针的一样严格
class CallBack
{public:CallBack(CallBackPtr fPtr, void *data_): func(fPtr), data(data_) {}void doCallBack() const throw();
private:CallBackPtr func; // function to call when// callback is madevoid *data; // data to pass to callback
}; void CallBack::doCallBack() const throw()
{func(data);
}void callBackFcn1(void *data_)
{printf(callBackFcn1\n);
};void callBackFcn2(void *data_) throw()
{printf(callBackFcn2\n);
};//函数指针作为形参
class Aclass
{
public:Aclass(void* data_) : vdata(data_){};void doSomething1(){printf(Aclass doSomething1\n);};void doSomething2(){printf(Aclass doSomething2\n);};void* getData(){return vdata;}
private:void* vdata;
};void myfunc(Aclass* ptr,void* data,bool (*compare)(void const* pd1, void const* pd2))
{if(compare(ptr-getData(),data))ptr-doSomething1();elseptr-doSomething2();
};bool compare_int(void const* pd1, void const* pd2)
{return (*(int*)pd1*(int*)pd2)?false:true;
}
//
float add(float fa, float fb)
{return fafb;
};
float sub(float fa, float fb)
{return fa-fb;
};
float mul(float fa, float fb)
{return fa*fb;
};const float abs_min_val 0.0000001;
float div(float fa, float fb)
{if((fbabs_min_val)(fb(-abs_min_val))){return fa/abs_min_val;}return fa/fb;
};typedef float (*operFunc)(float fa, float fb);enum Method{ADD0,SUB,MUL,DIV
};
//函数指针成员,注册函数
class BClass
{
public:BClass(){my_compare NULL;};typedef bool (*compare)(void const* pd1, void const* pd2);void my_compare_do(void const* cm1, void const* cm2){if(NULLmy_compare)printf(please register func first!\n);if(my_compare(cm1,cm2))printf(do it A\n);elseprintf(do it B\n);};void register_func(compare cp1){my_compare cp1;};compare my_compare;
};//类函数指针数组test(成员列表)
class OperateKey
{
public:OperateKey(){};enum Directions { FORWARD, BACK, UP, DOWN };OperateKey do_it(Directions direct_){(this-vtbl[direct_])();return *this;};typedef void (*Action)();static Action vtbl[];
};void forward()
{printf(forward\n);
};
void back()
{printf(back\n);
};
void up()
{printf(up\n);
};
void down()
{printf(down\n);
};OperateKey::Action OperateKey::vtbl[] {forward, back, up, down};int main(int argc, char* argv[])
{//char* (*pf1)(char * p1,char *p2);//定义一个函数指针变量等同于下一句char* (*pf1)(char*, char*);char p1[]abc;char p2[]bcd;//char* (*pf1)(char*, char*) fun1;//直接初始化pf1 fun1; //fun等同于char* (*)(char*, char*)printf(%s\n, (*pf1)(p1,p2));pfunc pf2fun2;printf(%s\n,(*pf2)(p1,p2));//pfunc pf3 0; //初始化为 0表示该指针不指向任何函数pf3 pf1;printf(%s\n,pf3(p1,p2)); //隐式调用pf3 pf2;printf(%s\n,(*pf3)(p1,p2)); //显式调用//FuncPtr pfuncs[2] {doSomething1,doSomething2};FuncPtr funcPtrArray[3];funcPtrArray[0] doSomething1; //可以直接用函数名funcPtrArray[1] doSomething2; //可以用函数名加上取地址符//注意doSomething3直接传递是类型不符合的它对于funcPtrArray是一个错误类型//c的reinterpret_cast 可以让你迫使编译器以你的方法去处理,//转换函数指针,C不保证所有的函数指针都被用一样的方法表示在一些情况下这样的转换会产生不正确的结果.//funcPtrArray[2] reinterpret_castFuncPtr(doSomething3);//g编译时生效funcPtrArray[2] reinterpret_cast(FuncPtr, doSomething3);//gcc或g编译时都生效for(int i0; i3; i){funcPtrArray[i]();}//指向重载函数的指针int (*pft)(int, int) func_test;printf(min_val:%d\n, (*pft)(6,7));//回调void *callBackData;CallBack c_instance(callBackFcn1,callBackData);c_instance.doCallBack();//int aval 10;int bval 8;Aclass a_obj(aval);myfunc(a_obj,bval,compare_int); ////operFunc operf[] {add,sub,mul,div};float afval 7.8, bfval 8.5;float ret_fval operf[Method::ADD](afval,bfval);printf(%0.4f\n,ret_fval);//注册函数BClass bObject;bObject.register_func(compare_int);bObject.my_compare_do(aval,bval);//类函数指针数组成员列表OperateKey myKeys;myKeys.do_it(OperateKey::FORWARD);myKeys.do_it(OperateKey::DOWN);return 0;
}编译
g test.cpp func.cpp -o test.exe 或者构建Makefile文件调用make指令
CX gBIN : .
TARGET : test.exe
FLAGS : Include : .
source : test.cpp func.cpp
$(TARGET) :$(CX) $(FLAGS) $(source) -I$(Include) -o $(BIN)/$(TARGET)clean:rm $(BIN)/$(TARGET) 测试程序运行输出如下