1. 非虚成员函数指针(非静态)。
取一个非静态成员函数的地址,如果该函数是非虚函数,则得到的是它在内存中的真正地址,但是该地址并不完全,需要绑定与具体的类实例(对象)之上,借助对象的地址(this指针)才可以被调用,例如:一个成员函数指针
1. double (Point::* pmf)();
经过初始化:
1. double (Point::*coord)() = &Point::getX;//或者这样初始化它:coord = &Point::getX;
这样来调用它:
1. (orgin.*coord)();//或者这样(ptr->*coord)();
调用会转化成:
1. (coord)(&origin);//或者(coord)(ptr);
静态成员函数由于没有this指针,所以指向它的指针是函数指针,而不是指向成员函数的指针。
2. 指向虚成员函数的指针。
当一个函数指针指向一个虚成员函数时,例如:
1. float (Point::*pmvf)() = &Point::z;//z为虚函数
2. Point *ptr = new Point3d;//Point3d为Point的子类
那么,当ptr通过该函数指针调用z时,多态机制仍会生效,也就是如下调用,调用的仍是Point3d的z()函数。
1. (ptr->*pmvf)();//效果等同于ptr->z();
这是因为,取去函数的地址,,得到的是其在虚表中的索引值,也就是对于如下类:
1. class Point {
2. public:
3. virtual ~ Point();
4. float x();
5. float y();
6. virtual float z();
7. };
&Point::~Point得到的结果是1,
&Point::x和&Point::y得到的是它们在内存中的地址,因为它们并非虚函数,而&Point::z结果为2,因为它位于虚表的第三个位置(索引从0开始),所以通过上面例子中的pmvf调用函数,会转化为:
1. (*ptr->vptr[(int)pvmf])(ptr);//调用Point3d::z()
3. 多重继承下,指向成员函数的指针。
由于多重继承(包括多重虚拟继承)涉及到,子类中可能存在多个虚表,this指针的可能需要调整偏移,书中举例了cfront的实现方法,引入一个结构体,在需要的时候保存上述内容,结构体如下:
1. struct __mptr {
2. int delta;//虚基类或者多重继承下的第二个以及之后基类的this指针偏移
3. int index;//虚函数索引,非虚函数此值为-1
4. union {
5. //非虚函数地址
6. int v_offset;//虚基类或者多重继承下的第二个以及之后基类的虚表位置
7. };
8. };
在此模型下,以下调用:
1. (ptr->*pmvf)();
会变成:
1. (pmvf.index < 0) ?
2. (*pmvf.faddr)(ptr)//非虚函数
3. : (*ptr->vptr[pmvf.index])(ptr);//虚函数
1. 对于单一表达式的多重调用:
对如下incline函数:
1. inline Point operator+ (const Point& lhs, const Point& rhs)
2. {
3. Point new_pt;
4. //x()函数为成员变量_x的get,set函数
5. return new_pt;
6. }
由于该函数只是一个表达式,在cfront中,则其第二或者后继的调用操作不会被扩展,会变成:
1. new_pt.x = lhs._x + x__5PointFV(&rhs);
没有带来效率上的提升,需要重写为:
1. new_pt.x(lhs._x + rhs._x);
2. inline函数对于形式(Formal)参数的处理。
有如下inline函数:
1. inline int min(int i, int j)
2. {
3. return i < j ? i : j;
4. }
如下三个调用:
1. inline int bar()
2. {
3. int minVal;
4. int val1 = 1024;
5. int val2 = 2048;
6.
7. //case 1
8. //case 2
9. //case 3
10.
11. return minVal;
12. }
对于case1,调用会直接展开:
1. minVal = val1 < val2 ? val1 : val2;
case2直接使用常量:
1. minVal = 1024;
对于case3,由于引发了参数的副作用,需要导入临时对象,以避免重复求值(注意下面逗号表达式的使用):
1. int t1;
2. int t2;
3.
4. minVal = (t1 = foo()), (t2 = bar() + 1), t1 < t2 ? t1: t2;
3. inline函数中引入了局部变量。
改写上述min函数,引入一个局部变量:
1. inline int min(int i, int j)
2. {
3. int minVal = i < j ? i : j;
4. return minVal;
5. }
对于如下调用:
1. int minVal;
2. int val1 = 1024;
3. int val2 = 2048;
4. minVal = min(val1, val2);
为了维护局部变量,会被扩展为:
1. int minVal;
2. int val1 = 1024;
3. int val2 = 2048;
4.
5. int __min_lv_minVal;//将inline函数局部变量mangling
6. minVal = (__min_lv_minVal = val1 < val2 ? val1: val2), __min_lv_minVal;
再复杂一些的情况,例如局部变量加上有副作用的参数,会导致大量临时性对象的产生:
1. minVal = min(val1, val2) + min(foo(), bar() + 1);
会被扩展成为,注意逗号表达式,由左至右计算各个分式,以最右端的分式值作为最终值传回:
1. int __min_lv_minVal_00;
2. int __min_lv_minVal_01;
3.
4. int t1;
5. int t2;
6.
7. minVal = (__min_lv_minVal_00 = val1 < val2 ? val1 : val2, __min_lv_minVal_00) +
8. (__min_lv_minVal_01 = (t1 = foo(), t2 = bar() + 1, t1 < t2 ? t1 : t2), __min_lv_minVal_01);
会产生多个临时变量,所以inline函数的使用必须要谨慎。