继续第四章的内容整理,这一部分也是第四章的最后一部分,是关于成员函数指针的,第三章下的内容整理是关于成员变量指针的,这个则是成员函数指针,二者可以互为参考。
1. 非虚成员函数指针(非静态)。
取一个非静态成员函数的地址,如果该函数是非虚函数,则得到的是它在内存中的真正地址,但是该地址并不完全,需要绑定与具体的类实例(对象)之上,借助对象的地址(this指针)才可以被调用,例如:一个成员函数指针
double (Point::* pmf)();
经过初始化:
double (Point::*coord)() = &Point::getX;//或者这样初始化它:coord = &Point::getX;
这样来调用它:
(orgin.*coord)();//或者这样(ptr->*coord)();
调用会转化成:
(coord)(&origin);//或者(coord)(ptr);
静态成员函数由于没有this指针,所以指向它的指针是函数指针,而不是指向成员函数的指针。
2. 指向虚成员函数的指针。
当一个函数指针指向一个虚成员函数时,例如:
float (Point::*pmvf)() = &Point::z;//z为虚函数
Point *ptr = new Point3d;//Point3d为Point的子类
那么,当ptr通过该函数指针调用z时,多态机制仍会生效,也就是如下调用,调用的仍是Point3d的z()函数。
(ptr->*pmvf)();//效果等同于ptr->z();
这是因为,取去函数的地址,,得到的是其在虚表中的索引值,也就是对于如下类:
class Point {
public:
virtual ~ Point();
float x();
float y();
virtual float z();
};
&Point::~Point得到的结果是1,
&Point::x和&Point::y得到的是它们在内存中的地址,因为它们并非虚函数,而&Point::z结果为2,因为它位于虚表的第三个位置(索引从0开始),所以通过上面例子中的pmvf调用函数,会转化为:
(*ptr->vptr[(int)pvmf])(ptr);//调用Point3d::z()
3. 多重继承下,指向成员函数的指针。
由于多重继承(包括多重虚拟继承)涉及到,子类中可能存在多个虚表,this指针的可能需要调整偏移,书中举例了cfront的实现方法,引入一个结构体,在需要的时候保存上述内容,结构体如下:
struct __mptr {
int delta;//虚基类或者多重继承下的第二个以及之后基类的this指针偏移
int index;//虚函数索引,非虚函数此值为-1
union {
ptrtofunc faddr;//非虚函数地址
int v_offset;//虚基类或者多重继承下的第二个以及之后基类的虚表位置
};
};
在此模型下,以下调用:
(ptr->*pmvf)();
会变成:
(pmvf.index < 0) ?
(*pmvf.faddr)(ptr)//非虚函数
: (*ptr->vptr[pmvf.index])(ptr);//虚函数
关于成员函数指针的内容就整理这些,之后还有一点关于inline函数的内容整理。