C++编程基础之函数指针

1、函数指针

    定义:函数指针是指指向函数的指针。像其他指针一样,函数指针也指向特定的类型。函数类型由其返回值以及形参表确定,而与函数名无关。

e.g   void (*pf) ( char,int );

这个语句将pf声明指向函数的指针,它所指向的函数带有一个char类型,一个int类型,返回类型为void。我们可以这样理解:我们怎么定义普通的指针呢,如我们定义一个int型的指针,int *p;是在变量声明前面加*,p前面加上*号。而我们定义函数指针要在函数声明前加*, 函数声明为void pf( char,int );函数声明前加*后变成void *pf(char,int);我们把*pf用小括号括起来,变成void (*pf) ( char,int ); 这就是函数指针的声明方法

测试代码如下:

#include"stdio.h"

void (*pf)(char, int);

void fun(char ,int);  //声明一个函数,形参为一个char类型,一个int类型,返回类型为void

int main()

{

    pf=fun;         //给函数指针pf赋值为fun函数的地址(函数名代表函数的地址)

    (*pf)('c',90);     //调用pf指向的函数

}

void fun(char a,int b)

{

    printf("the argument is %c and %d/n",a,b);

}

函数运行后的结果是

         The argumeng is c and 90

 

2、函数指针类型

    函数指针类型相当地冗长。使用typedef为指针类型定义同义词,可将函数指针的使用大大简化typedef  void (*FCN) (char,int);

 记忆方法:在函数指针声明 void (*FCN)(char,int)前加上typedef关键字就是函数指针类型的声明。该定义表示FCN是一种函数指针类型。该函数指针类型表示这样一类函数指针:指向返回void类型并带有一个char类型,一个int类型的函数指针。

测试代码如下:

#include

#include"stdio.h"

typedef void (*FCN)(char, int);     //声明一个函数指针类型

void fun(char ,int);  //声明一个函数,形参为一个char类型,一个int类型,返回类型为void

int main()

{

    FCN pf=NULL;           

    pf=fun;            //给函数指针pf赋值为fun函数的地址(函数名代表函数的地址)

    (*pf)('c',90);     //调用pf指向的函数

}

void fun(char a,int b)

{

    printf("the argument is %c and %d/n",a,b);

}

 

3、函数类型

函数类型的定义:

           typedef void (*FCN)(char, int);     //声明一个函数类型

该声明定义了一个函数类型,FCN表示这样一类函数,带有两个形参,一个是int ,一个是char,返回值是void型。一般用于函数声明和函数的形参。一般我们在调用函数时,应该先声明要调用函数,如我们调用fun函数,则应在调用的前面声明void fun(char ,int);如果我们定义了函数类型typedef void FCN(char , int);我们就可以这样声明函数原形,FCN fun;大大简化了函数原型的声明,函数类型用于形参的情况我们在下面讲解。

#include"stdio.h"

typedef void FCN(char , int);  

int main()

{

    FCN fun;        

    fun('c',90);  

}

void fun(char a,int b)

{

          printf("the argument is %c and %d/n",a,b);

}

4、通过指针调用函数

   指向函数的指针可用于调用它所指向的函数。可以不需要使用解引用操作符,直接通过指针调用此函数。

         void (*pf)(char, int);

         pf=fun;

两种调用方法          

         (*pf)('c',90); // 显式调用

         pf(c,90); // 隐式调用

测试代码如下:

#include"stdio.h"

void (*pf)(char, int); //声明一个函数指针,它所指向的函数形参带有一个char类型,一个int类型,返回类型为void

void fun(char ,int);  //声明一个函数,形参为一个char类型,一个int类型,返回类型为void

int main()

{

    pf=fun;      //给函数指针pf赋值为fun函数的地址(函数名代表函数的地址)

    (*pf)('c',90);     //调用pf指向的函数

    Pf(‘a’,80);

}

void fun(char a,int b)

{

printf("the argument is %c and %d/n",a,b);

}

5、返回指向函数的指针

函数可以返回指向函数的指针,但是,正确写出这种返回类型相当不容易:

         int (*ff(int))(int*,int);

要理解函数指针声明的最佳方法是从声明的名字开始由里而外理解。要理解该声明的含义,首先观察:

         ff(int)

ff声明为一个函数,它带领有一个int 型的形参。该函数返回 int (*)(int*,int);

它是一个指向函数的指针,所指向的函数返回int型并带有两个分别是int *型和int型的形参。使用typedef可使该定义更简明易懂:

         typedef int (*PF)(int *,int);

         PF ff(int);

允许将形参定义为函数类型,但函数的返回类型则必须是指向函数的指针,而不能是函数。具有函数类型的形参所对应的将被自动转换为指向相应函数类型的指针。但是,当返回的是函数时,同样的转换操作则无法实现。

         //func is a function type,not a pointer to function!

         typedef int func(int*,int)

         void f1(func); //ok: f1 has a parameter of function type

         func f2(int) ;//error: f2 has a return type of function type

         func * f3(int);//ok: f3 return a pointer to function type

6、在C++类中使用函数指针

函数指针可以再C++类中使用也可以是累的成员变量。在C++类中函数指针的定义与使用都需要加上了类限制对象,用来指明指针指向的函数是哪个类的。如下:

class CSimple

{

 public:

char lcFun(int a){ return; }

};

CSimple simple;

typedef char (CSimple::*PTRFUN)(int);

PTRFUN pFun;

 void main()

{

pFun = CSimple::lcFun;

simple.(*pFun)(2);

}

 

这里的类对象也可以是使用new得到的。如果使用类成员函数指针必须有“->*”“.*”的调用。

CSimple *psimple = new CSimple;

psimple ->(*pFun)(2);

delete psimple;

测试案例:

#include"stdio.h"

#include

class FuncPointer  

{

public:

void Func1() { printf("call Func1/r/n");}

void Func2() { printf("call Func2:/r/n");}

    static void Func3(){printf("call Func3:/r/n");}

public:  

    typedef void (*Fun)();  

    typedef void (FuncPointer::*MFun)();  

    MFun    m_Fun;  

};

 

typedef void (*G_Fun)();

void G_Fun1(){printf("call G_Fun1:/r/n");}

 

int main(int argc, char *argv[])  

{  

    //调用静态成员函数

G_Fun gFun1 = &FuncPointer::Func3; 

gFun1();  

 

    //调用非静态成员函数 

    //G_Fun gFun2 = &FuncPointer::Func1; // cannot convert `void (FuncPointer::*)()' to `void (*)()' in initialization

 

    //FuncPointer::Fun fun3 = &FuncPointer::Func1; //cannot convert `void (FuncPointer::*)()' to `void (*)()' in initialization

 

    FuncPointer::Fun fun4 = FuncPointer::Func3;  

    fun4();  

 

    FuncPointer::Fun fun5 = G_Fun1;  

    fun5();  

 

    // FuncPointer::MFun fun6 = G_Fun1; // cannot convert `void (*)()' to `void (FuncPointer::*)()' in initialization

    FuncPointer::MFun fun7 = &FuncPointer::Func1;  

    //fun7();     //  must use .* or ->* to call pointer-to-member function in `fun7 (...)'

    FuncPointer* fp = new FuncPointer();  

    (fp->*fun7)();                        

 

    fp->m_Fun = &FuncPointer::Func2;  

    //(fp->*m_Fun)();                        // `m_Fun' undeclared (first use this function)

    (fp->*(fp->m_Fun))();  

   

system("PAUSE");

return 0;  

} 

 

7、函数指针应用

应用一、

在开发某软件过程中遇到这样一个问题,前级模块传给我二进制数据,输入参数为 char* buffer int lengthbuffer是数据的首地址,length表示这批数据的长度。数据的特点是:长度不定,类型不定,由第一个字节(buffer[0])标识该数据的类型,共有25628 )种可能性。我的任务是必须对每一种可能出现的数据类型都要作处理,并且我的模块包含若干个函数,在每个函数里面都要作类似的处理。若按通常做法,会写出如下代码:

void MyFuntion( char* buffer, int length )

{   __int8 nStreamType = buffer[0];

switch( nStreamType )

{case 0:

function1();

break;

case 1:

......       

case 255:

function255();

break;

}

}

 

    如果按照这种方法写下去,那么在我的每一个函数里面,都必须作如此多的判断,写出的代码肯定很长,并且每一次处理,都要作许多次判断之后才找到正确的处理函数,代码的执行效率也不高。针对上述问题,我想到了用函数指针数组的方法解决这个问题。

既然函数名可以通过函数指针加以保存,那们也一定能定义一个数组保存若干个函数名,这就是函数指针数组。正确使用函数指针数组的前提条件是,这若干个需要通过函数指针数组保存的函数必须有相同的输入、输出值。

这样,我工作中所面临的问题可以解决如下:

首先定义256个处理函数(及其实现)

void funtion0( void );

……

void funtion255(void );

其次定义函数指针数组,并给数组赋值。

void (*fun[256])(void);

fun[0] = function0;

……

fun[255] = function();

最后,MyFunction()函数可以修改如下:

void MyFuntion( char* buffer, int length )

{

    __int8 nStreamType = buffer[0];

    (*fun[nStreamType]();

}

  只要2行代码,就完成了256case语句要做的事,减少了编写代码时工作量,将nStreamType作为数组下标,直接调用函数指针,从代码执行效率上来说,也比case语句高。假如多个函数中均要作如此处理,函数指针数组更能体现出它的优势。

 

应用二、

在调用动态库时,习惯用typedef重新定义动态库函数中的函数地址(函数指针),如在动态库(test.dll)中有如下函数:

      int   DoCase(int, long);

则,在调用动态库是有两种方法:

先声明一个与动态库中类型一致的指针函数变量:

int (*DOCASE)(int ,long);//用于指向动态库中的DoCase函数地址

HINSTANCE gLibMyDLL = NULL;

gLibMyDLL = LoadLibrary("test.dll");

if(gLibMyDLL != NULL)

{

//得到函数地址

DOCASE = (int(*)(int,long))GetProcAddress(gLibMyDLL, "DoCase");

}  

 

//调用函数

int s = DOCASE(1,1000);

typedef定义一个指针函数:

typedef (*DOCASE)(int ,long);

HINSTANCE gLibMyDLL = NULL;

DOCASE _docase;

gLibMyDLL = LoadLibrary("test.dll");

     if(gLibMyDLL != NULL)

{

_docase = (DOCASE)GetProcAddress(gLibMyDll, "DoCase");

}

//调用函数

int s=_docase(1,1000);

你可能感兴趣的:(c++)