C++函数指针

函数指针

       函数指针就是一个指向函数起始地址的指针变量。

普通函数指针

       C/C++语言中,函数相当于汇编中的参数入栈和调用proc,

函数就是将这两个过程封装起来。函数名就代表函数的起始地址。

要调用这个函数,就要找到对应的起始地址。有了起始地址就可进入带函数体中执行代码。

下面看几个例子:

typedef void (*Ps_Func0)(void);         //指向返回值为void和无参的函数
typedef void (*Ps_Func1)(int); //指向返回值为void和参数为int的函数
typedef void (*Ps_Func2)(int,int); //指向返回值为void和两个参数为int的函数

void func1(){cout<<"func1"<<endl;}
void func2(int a){cout<<"func2 a ="<<a<<endl;}
void func3(int a,int b){cout<<"func3 a = "<<a<<" b = "<<b<<endl;}
int func4(int a){cout<<"func4 a ="<<a<<endl;return 0;}

int main()
{
//将所有的参数为1的函数指针 指向不同类型的函数
Ps_Func1 pFunc1 = (Ps_Func1)func1;

Ps_Func1 pFunc2 = (Ps_Func1)func2;
Ps_Func1 pFunc3 = (Ps_Func1)func3;
Ps_Func1 pFunc4 = (Ps_Func1)func4;

//强制转换为不同的 函数指针
cout<<endl<<"*************func1*************"<<endl;

(*pFunc1)(1);
(*(Ps_Func0)(pFunc1))();
(*(Ps_Func1)(pFunc1))(1);
(*(Ps_Func2)(pFunc1))(1,2);

cout<<endl<<"*************func2*************"<<endl;
(*pFunc2)(1);
(*(Ps_Func0)(pFunc2))();
(*(Ps_Func2)(pFunc2))(1,2);

cout<<endl<<"*************func3*************"<<endl;
(*pFunc3)(1);
(*(Ps_Func0)(pFunc3))();
(*(Ps_Func1)(pFunc3))(1);
(*(Ps_Func2)(pFunc3))(1,2);

cout<<endl<<"*************func4*************"<<endl;
(*pFunc4)(1);
(*(Ps_Func0)(pFunc4))();
(*(Ps_Func1)(pFunc4))(1);
(*(Ps_Func2)(pFunc4))(1,2);

  return 0; 
}
//上面所有能编译通过吗? 或者那些地方会报错?
//VC6.0下,均编译通过,没有任何警告和error,执行也正确。
//如果不加强制转换,从语法的角度来说,是不能够的,因为类型不同,且都//是外部类型
//从内存和编译器角度来看,加上前置转换时没有任何问题的
//因为函数在本质就是汇编中的过程的封装,函数名就是这个过程的起始地址
//不同的函数名代就表了不同的地址
//既然都是地址,在本质上就都死相同的,任何指向函数的指针都可以指向任//何函数 (也可能编译器相关)

下面看一段C代码与汇编代码(摘自网络)

C代码:
int function(int a, int b)
{
return a + b;
}

void main()
{
function(10, 20);
}

 

汇编代码:(cdecall方式)
_function
push ebp
mov ebp, esp
mov eax, [ebp+8] ;参数1
add eax, [ebp+C] ;加上参数2
pop ebp
retn

_main
push ebp
mov ebp, esp
push 14h ;参数 2入栈
push 0Ah ;参数 1入栈
call _function ;调用函数
add esp, 8 ;修正栈
xor eax, eax
pop ebp
retn

 具体的可以体会一下。。。。

根据函数调用方式和参数压入顺序目前存在三种约定:

这都相关压栈顺序和栈的清理工作约定

他们的细节都不相同,但有一点是肯定的,参数比须从右向左压入栈中

Stdcall   函数必须自已清理栈

cdecall 由调用者清除堆栈 C的默认函数调用方式 所以这样C支持可变参数

fastcall                是把函数参数列表的前三个参数放入寄存器eax,edx,ecx,其他参数压栈

成员函数指针

1 定义

成员函数指针也是一样指向函数的指针,但是是作为类成员的方式。

 

class Base
{
public:
int func0(){cout<<"func0"<<endl;return 0;}
int func1(int a){cout<<"func1 a= "<<endl;return 0;}
};

//定义成员函数指针 (指向类成员函数,而不是类对象成员函数)
typedef int (Base::*PS_MFuc0)();

typedef int (Base::*PS_MFuc1)(int);

int main()
{
Base obj;
PS_MFuc0 pMfunc0;
pMfunc0 = (PS_MFuc0)(&Base::func1); //强制转换

(obj.*pMfunc0)(); //执行完 崩溃
return 0; 
}
//可成功链接,为何执行是崩溃?如果使用dynamic_cast或者static_cast
//进行转化,是无法通过的,可见类成员函数指针要求上是更严格的
//所以在C++中转换,尽量使用static_cast或者dynamic_cast进行。

 

二 继承

 

class Base
{
public:
int func0(){cout<<"Base func0"<<endl;return 0;}
int func1(int a){cout<<"Base func1 a= "<<endl;return 0;}
};

class App : public Base
{
public:
int func0(){cout<<"App func0"<<endl;return 0;}
int App_func0(){cout<<"App App_func0"<<endl;return 0;}
};
typedef int (Base::*PS_MFuc0)();
typedef int (Base::*PS_MFuc1)(int);

typedef int (App::*PS_App_MFuc0)();
typedef int (App::*PS_App_MFuc1)(int);


int main()
{
App appObj;
Base baseObj;

PS_MFuc0 pMfunc0;
PS_App_MFuc0 pAppMfunc;

//pMfunc0 = &App::App_func0; //error基类成员函数指针指向派生类成员函数指针
pAppMfunc = &Base::func0; //right 派生类成员函数指针指向基类成员函数指针
(appObj.*pAppMfunc)(); //执行 基类函数
  return 0; 
}

 

但是如果仅能支持基类向派生类转换,那在实现框架中是非常不利的。

 

通常编译器都是支持强制转换。强制将派生类的成员函数指针转换为基类的。

各种编译器对这种转换的支持和处理方式不一

 

int main()
{
App appObj;
Base baseObj;

PS_App_MFuc0 pAppMfunc;
PS_MFuc0 pMfunc0;

pAppMfunc = &Base::func0; //无需转换
pMfunc0 = static_cast<PS_MFuc0>(&App::func0); //需强制转换为基类

(appObj.*pAppMfunc)();
Base* pObj = static_cast<Base*>(&appObj);
  (pObj->*pMfunc0)(); //使用基类对象调用其派生类成员函数
 
  return 0; 
}  

 

这种情况应当是使用的比较多的,平台上随处可见。


三 虚函数

支持多态

 

class Base
{
public:
virtual int func0(){cout<<"Base func0"<<endl;return 0;}
int func1(int a){cout<<"Base func1 a= "<<endl;return 0;}
};

class App : public Base
{
public:
virtual int func0(){cout<<"App func0"<<endl;return 0;}
int App_func0(){cout<<"App App_func0"<<endl;return 0;}
};

typedef int (Base::*PS_MFuc0)();
typedef int (Base::*PS_MFuc1)(int);

typedef int (App::*PS_App_MFuc0)();
typedef int (App::*PS_App_MFuc1)(int);


int main()
{
App appObj;
Base baseObj;

PS_MFuc0 pMfunc0;
PS_App_MFuc0 pAppMfunc;

//pMfunc0 = &App::App_func0; //error基类成员函数指针指向派生类成员函数指针
pAppMfunc = &Base::func0; //right 派生类成员函数指针指向基类成员函数指针
(appObj.*pAppMfunc)(); //执行 派生类函数 注意与上面的不同
  return 0;
}

 


 

成员函数的调用需要:this(对象),成员函数(地址)

 

int main()
{
PS_App_MFuc0 pAppMfunc;
pAppMfunc = &Base::func0; //在对象定义之前 定义成员函数指针

App appObj;
Base baseObj;
  (appObj.*pAppMfunc)(); //能够执行正确函数
  return 0;
}


这里就有疑问了:在对象定义之前定义成员函数指针,但是这个时候对象还是不存在的,

那么之后定义了对象是如何通过成员函数指针正确找到要执行的函数?

这应当是和编译器和C++对象模型相关联的。(比较复杂太深奥不清楚这里不探究了)

 

参考文章:http://blog.csdn.net/xlie/article/details/3031966写的非常不错。

posted @ 2011-12-12 19:01  __Shadow  阅读(1243)  评论(0编辑  收藏  举报