0
点赞
收藏
分享

微信扫一扫

深入探索C++对象模型(六) Function语义学


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函数的使用必须要谨慎。

举报

相关推荐

0 条评论