C++ 函数指针的用法

来源:互联网 发布:广州网络优化 编辑:程序博客网 时间:2024/05/16 08:19

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

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

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

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

3.1 定义一个函数指针

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

int a=10;

int *pa=&a;

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

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, md;


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

以下给出三个函数指针定义的形式 :

int (*pFunction)(float,char,char) = NULL;  --------C语言函数和C++静态成员函数的函数指针

int (MyClass::*pMemberFunction)(float,char,char)=&MyClass::fucn1;  ----------------(指向非静态函数成员的函数指针), 引用 mc.*pMemberFunction(x,x,x)

int (MyClass::*pConstMemberFunction)(float,char,char) const=&MyClass::fucn2; ----------------(指向非静态函数成员的函数指针) 引用 md.*pMemberFunction(x,x,x)

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

首先,要记住一点的就是形式一定要具备完备性,能表达出我们所要表达的内容,即指向函数这个事实。我们知道普通变量指针可以指向对应类型的任何变量,同样函数指针也应该能够指向对应类型的任何变量。对应的函数类型靠什么来确定?这个我们可以想一下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++中,对于赋值,你必须要加“&”,而且你还必须再次之前已经定义好了一个类实例,取地址符号要操作于这个类实例的对应的函数成员上。在使用成员函数的指针调用成员函数时,你必须要加类实例的名称,然后再使用.*或者->*来使用成员函数指针。举例如下:


pMemberFunction= &MyClass::func1;  //必须要加取地址符号, notice, mc.func1 is not right

pConstMemberFunction = &MyClass::func2;

接下来,调用函数:

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

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

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

0 0