C++函数指针

</pre>                                            声明:本博客是参考了其他牛人的博客加上自己的理解整理而成。<p></p><p><span style="font-size:14px">                                            参考博客地址:<a target=_blank target="_blank" href="http://hipercomer.blog.51cto.com/4415661/792300">点击打开链接</a>。</span></p><p><span style="font-size:14px"></span></p><h1 style="color:rgb(85,85,85)">1 函数指针简单介绍</h1><h2 style="color:rgb(85,85,85)">1.1 什么是函数指针</h2><p></p><p style="color:rgb(85,85,85)">函数指针是一个指向函数的指针(呃,貌似是废话),函数指针表示一个函数的入口地址。使用函数指针的好处就是在处理“在运行时根据数据的具体状态来选择相应的处理方式”这种需求时更加灵活。</p><h2 style="color:rgb(85,85,85)">1.2 一个简单的例子</h2><p style="color:rgb(85,85,85)">下面是一个简单的使用函数指针取代switch-case语句的例子,为了能够比较出二者效率差异,所以在循环中进行了大量的计算。</p><pre name="code" class="cpp">/* 
 
*Author:Choas Lee 
 
*Date:2012-02-28 
 
*/ 
 
#include<stdio.h> 

#define N 1000000 
 
#define COE 1000000 
 
float add(float a,float b){return a+b;} 
 
float minus(float a,float b){return a-b;} 
 
float multiply(float a,float b){return a*b;} 
 
float divide(float a,float b){return a/b;} 
 
typedef float (*pf)(float,float); 
 
void switch_impl(float a,float b,char op) 
 
{ 
 
      float result=0.0; 
 
      switch(op) 
 
      { 
 
             case '+': 
 
                    result=add(a,b); 
 
                    break; 
 
             case '-': 
 
                    result=minus(a,b); 
 
                    break; 
 
             case '*': 
 
                    result=multiply(a,b); 
 
                    break; 
 
             case '/': 
 
                    result=divide(a,b); 
 
                    break; 
 
      } 
 
} 
 
void switch_fp_impl(float a,float b,pf p) 
 
{ 
 
      float result=0.0; 
 
      result=p(a,b); 
 
} 
 
int conversion(struct timeval tmp_time) 
 
{ 
 
      return tmp_time.tv_sec*COE+tmp_time.tv_usec; 
 
} 
 
int main() 
 
{ 
 
      int i=0; 
 
#if defined(UNIXENV) 
 
      struct timeval start_point,end_point; 
 
  
 
      gettimeofday(&start_point,NULL); 
 
#endif 
 
      for(i=0;i<N;i++) 
 
      { 
 
             switch_impl(12.32,54.14,'-'); 
 
      } 
 
#if defined(UNIXENV) 
 
      gettimeofday(&end_point,NULL); 
 
      printf("check point 1:%d\n",conversion(end_point)-conversion(start_point)); 
 
      
 
      gettimeofday(&start_point,NULL); 
 
#endif 
 
      for(i=0;i<N;i++) 
 
      { 
 
             switch_fp_impl(12.32,54.14,minus); 
 
      } 
 
#if defined(UNIXENV) 
 
      gettimeofday(&end_point,NULL); 
 
      printf("check point 2:%d\n",conversion(end_point)-conversion(start_point)); 
 
#endif 
 
      return 0; 
 
} 

运行结果如下:

[lichao@sg01 replaceswitch]$ ./replaceswitch 
 
check point 1:22588 
 
check point 2:19407 
 
[lichao@sg01 replaceswitch]$ ./replaceswitch 
 
check point 1:22656 
 
check point 2:19399 
 
[lichao@sg01 replaceswitch]$ ./replaceswitch 
 
check point 1:22559 
 
check point 2:19380 
 
[lichao@sg01 replaceswitch]$ ./replaceswitch 
 
check point 1:22181 
 
check point 2:19667 
 
[lichao@sg01 replaceswitch]$ ./replaceswitch 
 
check point 1:22226 
 
check point 2:19813 
 
[lichao@sg01 replaceswitch]$ ./replaceswitch 
 
check point 1:22141 
 
check point 2:19893 
 
[lichao@sg01 replaceswitch]$ ./replaceswitch 
 
check point 1:21640 
 
check point 2:19745 

从上面可以看出,使用函数指针:(一)在某种程度上简化程序的设计(二)可以提高效率。在这个例子中,使用函数指针可以提高 10% 的效率。

3 C/C++函数指针的语法

从语法上讲,有两种不兼容的函数指针形式:

(1)    指向C语言函数和C++静态成员函数的函数指针

(2)    指向C++非静态成员函数的函数指针

不兼容的原因是因为在使用C++非静态成员函数的函数指针时,需要一个指向类的实例的this指针,而前一类不需要。

3.1 定义一个函数指针

指针是变量,所以函数指针也是变量,因此可以使用变量定义的方式来定义函数指针,对于普通的指针,可以这么定义:

int a=10;

int *pa=&a;

这里,pa是一个指向整型的指针,定义这个指针的形式为:

int * pa;

区别于定义非指针的普通变量的“形式”就是在类型中间和指针名称中间加了一个“*”,所以能够表达不同的“内容”。这种形式对于表达的内容是完备的,因为它说明了两点:(1)这是一个指针(2)这是一个指向整型变量的指针

以下给出三个函数指针定义的形式,第一个是C语言的函数指针,第二个和第三个是C++的函数指针的定义形式(都是指向非静态函数成员的函数指针):

int (*pFunction)(float,char,char)=NULL;

int (MyClass::*pMemberFunction)(float,char,char)=NULL;

int (MyClass::*pConstMemberFunction)(float,char,char) const=NULL;

我们先不管函数指针的定义形式,如果让我们自己来设计指向函数的函数指针的定义形式的话,我们会怎么设计?

首先,要记住一点的就是形式一定要具备完备性,能表达出我们所要表达的内容,即指向函数这个事实。我们知道普通变量指针可以指向对应类型的任何变量,同样函数指针也应该能够指向对应类型的任何变量。对应的函数类型靠什么来确定?这个我们可以想一下C++的函数重载靠什么来区分不同的函数?这里,函数类型是靠这几个方面来确定的:(1)函数的参数个数(2)函数的参数类型(3)函数的返回值类型。所以我们要设计一种形式,这种形式定义的函数指针能够准确的指向这种函数类型的任何函数。

在C语言中这种形式为:

返回类型 (*函数指针名称)(参数类型,参数类型,参数类型,…);

嗯,定义变量的形式显然不是我们通常见到的这种形式:

类型名称 变量名称;

但是,这也是为了表达函数这种相对复杂的语义而不得已采用的非一致表示形式的方法。因为定义的这个函数指针变量,能够明确的表达出它指向什么类型的函数,这个函数都有哪些类型的参数这些信息,确切的说,它是完备的。你可能会问为什么要加括号?形式上讲能不能更简洁点?不能,因为不加括号就会产生二义性:

返回类型 *函数指针名称(参数类型,参数类型,参数类型,…);

这样的定义形式定义了一个“返回类型为‘返回类型*’参数为(参数类型,参数类型,参数类型,…)的函数而不是函数指针了。

接下来,对于C++来说,下面这样的定义形式也就不难理解了(加上类名称是为了区分不同类中定义的相同名称的成员函数):

返回类型 (类名称::*函数成员名称)(参数类型,参数类型,参数类型,….

3.2 函数的调用规则

一般来说,不用太关注这个问题。调用规则主要是指函数被调用的方式,常见的有_stdcall,_fastcall,_pascal,_cdecl等规则。不同的规则在参数压入堆栈的顺序是不同的,同时在有调用者清理压入堆栈的参数还是由被调用者清理压入堆栈的参数上也是不同的。一般来说,如果你没有显式的说明调用规则的话,编译器会统一按照_cdecl来处理。

3.3 给函数指针赋值和调用

给函数指针赋值,就是为函数指针指定一个函数名称。这个过程很简单,下面是两个例子:

int func1(float f,int a,int b){return f*a/b;}

int func2(float f,int a,int b){return f*a*b}

然后我们给函数指针pFunction赋值:

pFunction=func1;

pFunction=&func2;

上面这段代码说明了两个问题:(1)一个函数指针可以多次赋值(想想C++中的引用)(2)取地址符号是可选的,却是推荐使用的。

我们可以思考一下为什么取地址符号是可选的,在普通的指针变量赋值时,如上面所示,需要加取地址符号,而这里却是可选的?这是由于要同时考虑到两个因素(1)避免二义性(2)形式一致性。在普通指针赋值,需要加取地址符号是为了区别于将地址还是将内容赋给指针。而在函数赋值时没有这种考虑,因为这里的语义是清晰的,加上&符号是为了和普通指针变量一致---“因为一致的时候就不容易出错”。

最后我们来使用这个函数

pFunction(10.0,’a’,’b’);

(*pFunction)(10.0,’a’,’b’);

       上面这两种使用函数指针调用函数的方式都是可以的,原因和上面一样。

下面来说明C++中的函数指针赋值和调用,这里说明非静态函数成员的情况,C++中规则要求的严格的多了。让我感觉C++就像函数指针的后爸一样,对函数指针要求特别死,或许是因为他有一个函数对象这个亲儿子。

在C++中,对于赋值,你必须要加“&”,而且你还必须再次之前已经定义好了一个类实例,取地址符号要操作于这个类实例的对应的函数成员上。在使用成员函数的指针调用成员函数时,你必须要加类实例的名称,然后再使用.*或者->*来使用成员函数指针。举例如下:

MyClass

{

public:

      int func1(float f,char a,char b)

      {

             return f*a*b;

      }

      int func2(float f,char a,char b) const

      {

return f*a/b;
      }

}

首先来赋值:

MyClass mc;

pMemberFunction= &mc.func1;  //必须要加取地址符号

pConstMemberFunction = &mc.func2;

接下来,调用函数:

(mc.*pMemberFunction)(10.0,’a’,’b’);

(mc.*pConstMemberFunction)(10.0,’a’,’b’);

我感觉,C++简直在虐待函数指针啊。

下面是一个完整的例子:

/* 
*Author:Choas Lee 
 *Date:2012-02-28 
*/ 
#include<stdio.h> 
float func1(float f,char a,char b)  
{ 
      printf("func1\n"); 
      return f*a/b; 
} 
 
float  func2(float f,char a,char b) 
{ 
      printf("func2\n"); 
      return f*a*b; 
} 
 
class MyClass  
{ 
public: 
      MyClass(float f) 
      { 
             factor=f; 
      } 
 
      float func1(float f,char a,char b)
      { 
             printf("MyClass::func1\n"); 
             return f*a/b*factor;      
      } 
      float func2(float f,char a,char b) const 
      { 
             printf("MyClass::func2\n"); 
             return f*a*b*factor; 
      } 
private: 
      float factor; 
}; 
 
int main(int argc,char *argv[]) 
{ 
      float (*pFunction)(float,char,char)=NULL; 
      float (MyClass::*pMemberFunction)(float,char,char)=NULL; 
      float (MyClass::*pConstMemberFunction)(float,char,char)const=NULL; 
      float f=10.0; 
      char a='a',b='b'; 
      float result; 
 
      pFunction=func1;  //
      printf("pointer pFunction's address is:%x\n",pFunction); //地址
      result=(*pFunction)(f,a,b); //调用函数指针
      printf("result=%f\n",result); 
      pFunction=&func2;   //可以加也可以不加取地址符号。
      printf("pointer pFunction's address is:%x\n",pFunction); 
      result=pFunction(f,a,b); <span style="font-family: 宋体;">//可以加也可以不加*。</span>

      printf("result=%f\n",result); 
      if(func1!=pFunction) 
          printf("not equal.\n"); 
 
     pMemberFunction=&MyClass::func1; //C++成员函数赋值
 
      MyClass mc1(0.2); 
      printf("pointer pMemberFunction's address is:%x\n",pMemberFunction); 
 
      result=(mc1.*pMemberFunction)(f,a,b); //调用
      printf("result=%f\n",result); 
 
      pConstMemberFunction=&MyClass::func2; 
      MyClass mc2(2); 
      printf("pointer pConstMemberFunction's address is:%x\n",pConstMemberFunction); 
      result=(mc2.*pConstMemberFunction)(f,a,b); 
        printf("result=%f\n",result); 
      return 0; 
} 

运行结果为:

 

pointer pFunction's address is:400882 

 func1 
     result=9.897959 
    pointer pFunction's address is:400830 
   func2 
 
result=95060.000000 
 
not equal. 
 
pointer pMemberFunction's address is:400952 
 
MyClass::func1 
 
result=1.979592 
 
pointer pConstMemberFunction's address is:4008f2 
 
MyClass::func2 
 
result=190120.000000 

注意:上面的代码还说明了一点就是函数指针的一些基本操作,函数指针没有普通变量指针的算术操作,但是可以进行比较操作。如上面代码所示。

使用类的静态函数成员的函数指针和使用C语言的函数很类似,这里仅仅给出一个例子和其执行结果:

程序代码为:

/* 
 
*Author:Chaos Lee 
 
*Date:2012-02-28 
 
*/ 
 
#include<iostream> 
class MyClass 
{  
public: 
      static float plus(float a,float b) 
      { 
             return a+b; 
      }     
}; 
int main() 
{ 
      float result,a=10.0,b=10.0; 
      float (*p)(float,float); 
      p=&MyClass::plus; 
      result=p(a,b); 
      printf("result=%f\n",result); 
      return 0; 
 
} 
执行结果为:
result=20.00000






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值