设为首页 加入收藏

TOP

c/c++:回调函数(一)
2015-07-24 05:45:30 来源: 作者: 【 】 浏览:12
Tags:c/c 函数

1:函数名为指针

首先,在C语言中函数是一种function-to-pointer的方式,即对于一个函数,会将其自动转换成指针的类型.如:

复制代码
 1 #include
  
   
 2 
 3 void fun()
 4 {
 5 }
 6 
 7 int main()
 8 {
 9    printf("%p %p %p\n", &fun, fun, *fun);
10    return 0;
11 }
  
复制代码

这三个值的结果是一样的. 其实对于最后的那个*fun, 即使前面加上很多个*号, 其结果也不变, 即**fun, ***fun的结果都是一样的. 对于这个问题, 因为之前讲过函数是一种function-to-pointer方式, 其会自动转换成指针的类型, &fun是该函数的地址, 为指针类型, fun是一个函数, 会转换成其指针类型, 而对于*fun, 由于fun已经变成了指针类型, 指向这个函数, 所以*fun就是取这个地址的函数, 而又根据function-to-pointer, 该函数也转变成了一个指针, 所以以此类推, 这三个值的结果是相同的.

2:回调函数

通过将回调函数的地址传给调用者从而实现动态调用不同的函数。因此当我们想通过一个统一接口实现不同的内容,这时用回掉函数非常合适。

若要实现回调函数,最关键的是要把调用函数的参数定义为函数指针类型。函数指针的定义这里稍
微提一下。比如:
int (*ptr)(void); 这里ptr是一个函数指针,其中(*ptr)的括号不能省略,因为括号的优先级高于星号,那样就成了一个返回类型为整型的函数声明了。int为返回类型,括号内为函数的参数。

下面通过一个例子来解释回调函数的用法:

复制代码
 1 #include 
  
    
 2 #include 
   
     3 int Test1(int num) 4 { 5 printf("i am test1,the data is %d \n",num); 6 return 0; 7 } 8 int Test2(int num) 9 { 10 printf("i am test2,the data is %d\n",num); 11 return 0; 12 } 13 14 int Caller(int (*ptr)(int n),int n)//指向函数的指针作函数参数,这里第二个参数是函数指针的参数 15 { //不能写成void Caller2(int (*ptr)(int n)),这样的定义语法错误。 16 int a=(*ptr)(n); 17 return a; 18 } 19 int main() 20 { 21 22 Caller(Test1,20); 23 printf("************************\n"); 24 Caller(Test2,10); 25 26 return 0; 27 } 
   
  
复制代码

下面介绍几种比较容易混淆的指针概念:

1:函数指针

1:函数指针的定义方式:

  返回值类型 (* 指针变量名)(形参列表);

返回值为指针的函数定义: 返回指针类型 * 函数名(形参列表);

2:函数指针的赋值:

  在赋值时,可以直接将函数指针指向函数名(函数名即代表该段代码的首地址),但是前提是:函数指针和它指向的函数的参数个数以及类型必须一致。函数指针的返回值类型与函数的返回值类型必须一致。

3:通过函数指针调用函数:

  加上指针f指向函数func。(*f ) 和 func代表同一函数。

  使用方法如下:

  声明函数指针:int (*f)(int x);

  函数指针赋值: f=func ( int func(int x));

  函数指针调用函数: (*f)(x) (x为整型变量)

2:函数指针数组

函数指针数组是一个其元素是函数指针的数组。即,此数据结构是是一个数组,且其元素是一个指向函数入口地址的指针。

定义方式: 返回值 ( *数组名[个数]) (参数列表)

3:指向数组的指针

类型 (*变量名)[元素个数]

4: 指针数组

  类型 *变量名[元素个数]

  因为[] 比*具有更好的优先级。所以如果是变量a先和*结合则表示其为一个指针,如果a先和[]结合,则表示是一个数组。

带参数的回调函数:

复制代码
//定义带参回调函数
void PrintfText(char* s) 
{
    printf(s);
}

//定义实现带参回调函数的"调用函数"
void CallPrintfText(void (*callfuct)(char*),char* s)
{
    callfuct(s);
}

//在main函数中实现带参的函数回调
int main(int argc,char* argv[])
{
    CallPrintfText(PrintfText,"Hello World!\n");
    return 0;
}
复制代码

c++回调机制:

非静态成员函数作回调函数

当然如果是静态成员函数就好办跟全局函数是类似,到此为止世界还没有变乱,如在VC编程中用AfxBeginThread开启一个线程,就经常将参数AFX_THREADPROC pfnThreadProc定义为一个全局函数或静态成员函数,可是这两个都不方便访问类的非静态成员,之所以郑重其事地写这篇文章,就是以前静态回调用起来非常不爽。

回调函数是非静态成员函数呢?我们可不能简单地设为这样:

复制代码 复制代码 class CCallback
{
public:
void Func(int a)
{
cout<<"member function callback called with para="< }
};
typedef void (CCallback::*pMemberFunc)(int);
void Caller(pMemberFunc p)
{
(*p)(1);
} 复制代码 复制代码

这样编译就不会通过的,因为非静态的成员函数必须通过对象来访问,好,我们稍稍改进一下:

复制代码 复制代码 class CCallback
{
public:
void Func(int a)
{
cout<<"member function callback called with para="< }
};
typedef void (CCallback::*pMemberFunc)(int);
void Caller(CCallback* pObj,pMemberFunc p)
{
(pObj->*p)(1);
}

int main(int argc, char* argv[])
{
CCallback obj;
Caller(&obj,&CCallback::Func);
} 复制代码 复制代码

即给Caller多传个对象进去,好吧,貌似问题解决了,可是,调用者(如库的提供商)只知道回调函数接口长这样而已,事先全然不知客户的类是如何定义,终于模板登上场了:

template
void Caller(T* pObj,void (T::*p)(int))
{
(pObj->*p)(1);
}

其他不变的,把调用者这里换成模板就OK了,当然这个Caller也可以是成员函数,现在用这个方法写个小应用是没什么问题了,但是限制多多,如调用者一次只调用了一个实现,但现实情况往往是产生某个事件时,应该依次调用多个行为,即把挂在这个事件上的所有回调函数通通临幸一遍,还有回调是如此的重要,以至于C#不用库在语言本身层面就实现了它,我们也不可以到此草草了事,而是按照组件化的思维提供一套完善的回调机制,所谓完善,如上个例子中Caller只能接收一个参数为int,返回值为void的成员函数指针,等等,必须是这样的接口吗,想想参数为double行不行,如void (T::*p)(double)这样的函数传给它可以吗,int不是可自动转换为double吗,那这个函数指针也能自动转换吗,就像C#中的协变与逆变一样,不行,C+

首页 上一页 1 2 3 4 下一页 尾页 1/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇UVA12716 GCD XOR 数论数学构造 下一篇UVA 10883 Supermean 上下取对数..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: