天天看点

[读书笔记] 深入探索C++对象模型-第四章-Function语义学(下)

继续第四章的内容整理,这一部分也是第四章的最后一部分,是关于成员函数指针的,第三章下的内容整理是关于成员变量指针的,这个则是成员函数指针,二者可以互为参考。

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函数的内容整理。

继续阅读