您的位置:首页 > 其它

函数指针,指向成员函数的指针与指向成员变量的指针

2011-06-01 13:48 302 查看
1.从函数指针说起

可以声明一个指向特定函数的指针:

void (*fp)(int); //指向函数的指针(函数指针)

注意:其中的括号是必不可少的,它表明fp是一个指向返回值为void的函数的指针,而不是返回值为void*的函数。

void* fp(int); //声明一个返回值为void*的函数(指针函数)

函数指针的赋值:指向函数的指针可以为空,否则它就应该指向一个具有适当类型的函数。(与特定的函数的声明完全一样)

void (*fp)(int);

int f(int);

void g(long);

void h(int);

/////

fp = f; //错误;&f 的类型为int (*)(int)

fp = g;//错误;&g的类型为void (*)(long)

fp = 0;//正确,设为NULL

fp = h;//正确,指向h

fp = &h;//正确,明确赋予函数地址

注意:将一个函数的地址初始化或赋值给一个指向函数的指针时,无需显式的取得函数地址,编译器知道隐式的获得函数的地址,因此在这种情况下&操作符是可有可无的,通常省略不用。类似的,为了调用函数指针所指向的函数而对指针进行解引用操作也是不必要的,因为编译器可以帮你解引用。

(*fp)(12); //显式的解引用

fp(12); //隐式的解引用,结果相同

一个函数指针指向内联函数(inline function)是合法的。然而,通过函数指针调用内敛函数将不会导致内联式的函数调用。因为编译器通常无法再编译期精确的确定将会调用什么函数。因此在调用点,编译器别无他法,只好生成间接的非内联的函数调用代码。(函数指针就是指向函数的地址,如果编译器在编译时把指向的函数识别为内联函数,那内联函数就会把函数体原地展开,而不存在所谓的函数调用了,那函数指针该指向哪呢,可以这么理解)

参考:http://book.51cto.com/art/201011/232490.htm

2.指向成员函数的指针和指向成员变量的指针

这里成员函数可分为:非静态成员函数,静态成员函数和虚拟成员函数。

成员变量可分为:非静态成员变量和静态成员变量。

我们都知道,在C++程序被编译时,类的成员函数的实现被放置在代码段,与类的实例是分开存放的,成员函数供所有类的实例共享。然而,不同性质的函数被我们调用的方式是不同的。

静态成员函数可以说和我们使用的一般的函数基本没有区别。只不过静态成员函数的作用域限定在类中。我们可以定义一个普通的函数指针,然后让它指向静态成员函数,接下来我们就可以对函数指针进行引用了。

非静态成员函数和一般的函数有很大的区别。我们不能定义一个普通的函数指针,让它指向非静态的成员函数。

具体请看代码:

#include <iostream>
using namespace std;

class A;

typedef int (A::*Func)(); //定义一种函数指针类型,为指向类中的非静态成员函数,这里要注意A::

int (*outer_func)(); //在类外定义一个函数指针,可以指向类中的静态成员函数

class A
{
public:
static int sum();
int sub();
void functest();
A(int a1,int b1,int c1,int d1):inner_func2(sub),c(c1),d(d1)  //
{
a = a1;
b = b1;
}
Func inner_func1;  //定义一个函数指针,指向类中非静态成员函数,在main函数中进行赋值
public:
Func inner_func2;  //定义一个函数指针,指向类中非静态成员函数,在构造函数中初始化
static int a;   //定义静态变量
static int b;

int c;      //定义非静态变量
int d;
};

int A::a = 0;  //初始化静态变量
int A::b = 0;

int A::sub()
{
return c - d;
}

int A::sum()
{
return a + b;
}

void A::functest()  //测试指向类的成员函数的指针
{
cout << (this->*inner_func2)() << endl;
}

int main()
{
printf("&A::sum = %p/n",&A::sum); //打印类中成员函数地址,包括静态和非静态
printf("&A::sub = %p/n",&A::sub);

printf("&A::a = %p/n",&A::a);  //打印类的成员变量,包括静态和非静态
printf("&A::b = %p/n",&A::b);
printf("&A::c = %p/n",&A::c);
printf("&A::d = %p/n",&A::d);

A object(2,3,4,5);  //定义一个对象

printf("object.sum = %p/n",object.sum); //打印对象可访问类成员函数地址,包括静态和非静态
printf("object.sub = %p/n",object.sub);

printf("&object.a = %p/n",&object.a);  //打印对象可访问的类的成员变量,包括静态和非静态
printf("&object.b = %p/n",&object.b);
printf("&object.c = %p/n",&object.c);
printf("&object.d = %p/n",&object.d);

outer_func  = &A::sum;  //为函数指针赋值,只能赋值静态成员函数地址
cout << outer_func() << endl;  //测试函数指针

object.functest();  //测试函数指针

object.inner_func1 = &A::sub;  //为函数指针赋值,只能赋值非静态成员函数地址
cout << (object.*(object.inner_func1))() << endl;  //测试函数指针,这里与上面的outer_func相区别,一个定义在类中,一个定义在类外

return 0;
}


运行结果为:

&A::sum = 00401109

&A::sub = 004010D7

&A::a = 0047CDF0

&A::b = 0047CDF4

&A::c = 00000008

&A::d = 0000000C

object.sum = 00401109

object.sub = 004010D7

object.a = 0047CDF0

object.b = 0047CDF4

object.c = 0012FF78

object.d = 0012FF7C

5

-1

-1

从上面的结果可知静态成员函数和非静态成员函数放在一起,都在代码区中。静态成员变量放在静态数据区中,所以在定义对象object之前,就可以获取a和b的地址,然而,我们使用&A::c和&A::d获取的是非静态成员变量在类的实例中的偏移,上面两个函数指针占8个字节,所以得到c和d的偏移为8和12。当我们定义对象后,我们使用object.c和object.d就可以获取c和d在内存中的实际地址。

后面对函数指针的测试,可以看出静态成员函数与非静态成员函数的区别。

3. 指向虚拟成员函数的指针

那么虚拟机制能够在使用“指向成员函数的指针”的情况下运行吗?答案是肯定的。对一个非静态成员函数取地址,将得到该函数在内存中的实际地址。然而,面对一个虚拟函数,其地址在编译时期是未知的,所能知道的仅是虚拟函数在其相关虚拟函数表(virtual table)中的索引值。也就是说,对一个虚拟成员函数(virtual member function)取其地址,所能获得只是一个索引值。

但是虚拟机制的实施完全由编译器来控制。

参考:《深入探索C++对象模型》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: