取一个非静态成员函数的地址,如果该函数是nonvirtual,则得到的结果是它在内存中真正的地址。然而这个值也不是完全的,它也需要被绑定于某个class object的地址上,才能够通过它调用该函数。所有的非静态成员函数都需要对象的地址(以参数this指出)。
一个指向成员函数的指针,其声明语法如下所示:
double //return type
(Point::* //class the function is member
pmf) // name of pointer to member
(); // argument list
然后我们可以这样定义并初始化该指针:
double (Point::*coord)() = &Point::x;
也可以这样指定其值:
coord = &Point::y;
想调用它,可以这样做:
(origin.*coord)();或 (ptr->*coord)();
指向member function的指针的声明语法,以及指向“member selection运算符”的指针,其作用是作为this指针的空间保留者。这也就是为什么static member functions(没有this指针)的类型为“函数指针”,而不是“指向member function之指针”的原因。使用一个成员函数的指针,如果并不用于虚函数、多重继承、虚基类等情况的话,并不会比使用一个“nonmember function 指针”的成本更高。
支持“指向virtual member functions”的指针
注意下面的程序片段:
float (Point::*pmf)() = &Point::z;
Point *ptr = new Point3d;
pmf,一个指向成员函数的指针,被设置为Point::z()(一个虚函数)的地址。如果我们直接经由ptr调用z():
ptr->z();
则被调用的是Point3d::z()。但如果我们从pmf间接调用z()呢?
(ptr->*pmf)();
仍然是Point::z()被调用吗?也就是说,虚拟机制仍然能够在使用“指向成员函数的指针”的情况下正常运行吗?如何实现的呢?
面对一个虚函数,其地址在编译时期是未知的,所能知道的仅仅是虚函数在其相关的虚表中的索引值。也就是说,对一个虚成员函数取其地址,所能获得的只是一个索引值。假设我们有以下的Point声明:
Class Point{
public:
virtual ~Point();
float x();
float y();
virtual float z();
//…
}
然后取destructor的地址:
&Point::~Point;
得到的结果是1。取x()或y()的地址:
&Point::x();
&Point::y();
得到的是函数在内存中的地址,因为它们不是virtual。取z()的地址:
&Point::z();
得到的结果是2,通过pmf来调用z(),会被内部转化为一个编译时期的式子,一般形式如下:
( *ptr->vptr[(int)pmf] )( ptr );
对一个指向成员函数的指针评估求值,会因为该值有两种意义而复杂化,其调用操作也将有别于常规调用操作。pmf的内部定义,也就是:
float (Point::*pmf)();
必须允许该函数能够寻址出nonvirtual x()和virtual z()两个成员函数,这两个成员函数有着相同的原型,只不过其中一个代表内存地址,另外一个代表在对应虚表中的索引值。因此,编译器必须定义pmf使它能够(1)含有两种数值,(2)更重要的是其数值可以被区别代表内存地址还是虚表中的索引值。
在cfront2.0的非正式版本中,这两个值被内含在一个普通的指针内。它使用如下技巧:
(((int)pmf) & ~127)
? (*pmf)(ptr) //non-virtual invocation
: ( *ptr->vptr[(int)pmf] )( ptr ); //virtual invocation
这种实现技巧必须假设继承体系中最多只能够有128个虚函数。这并不是我们所希望的,但却证明是可行的。
在多重继承之下,指向Member Functions的指针
《深入理解C++对象模型》P178~180
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/wuliming_sc/archive/2009/01/31/3855694.aspx