深度探索C++对象模型笔记之四――函数语意学(上)

本章由下面问题引出:
当存在如下代码:
float 
   
Point3d::magnitude() const 
   
{ 
   
   return sqrt( 
   
         _x * _x + _y * _y + _z * _z 
   
   ); 
   
} 
   
 
Point3d Point3d::normalize() const 
   
{ 
   
   register float mag = magnitude(); 
   
   Point3d normal; 
   

   
     
   
   normal._x = _x/mag; 
   
   normal._y = _y/mag; 
   
   normal._z = _z/mag; 
   

   
     
   
   return normal; 
   
} 
   
 
Point3d obj; 
   
Point3d *ptr = &obj; 
   
 
进行这样函数调用:
obj.normalize();
ptr->normalize();
会发生什么事?
 
答案是:不确定。因为 C++ 支持三种类型的成员函数: nostatic static virtual ,每种类型的函数的被调用的方式不同!
 

4.1 Member的各种调用方式

1 )非静态成员函数
C++ 设计准则之一:非静态成员函数至少必须和一般的非成员函数有相同的效率。
 
方法是:编译器内部已将“成员函数实体”转换为“非成员函数实体”
 
转换步骤:
1.       改写函数的 signature (函数原型),即安插一个额外形参,该参数为 this 指针。对于非静态非 const 的成员函数,安插的 this 指针是 const 的,即是 X *const this X 为一个类;对于对于非静态 const 的成员函数,安插的 this 指针是 const 且所指对象也是 const 的,即 const X *const this 。这也是“ const 对象、指向 const 对象的指针或引用只能用于调用其 const 成员函数,如果尝试用它们调用非 const 成员函数,则是错误的( C++ Primer 4th 7.7.1 )”的原因。
2.       将函数体中“对 nonstatic data member ”的存取操作改为经由 this 指针来存取。
3.       member function 重新写成一个外部函数,对函数名进行处理,使它在程序中成为独一无二的名称。
经过上述处理, normalize 函数变成类似下面的外部函数 ( 假设 Point3d copy constructor 已声明,NRV优化已实施)
extern void normalize_ _7Point3dFv(
   register const Point3d *const this,
Point3d &__result ) /* 多一个 __result 参数的原因 P63――P71*/
{
   register float mag = this->magnitude();
 
   // default constructor
   __result.Point3d::Point3d();
 
   __result._x = this->_x/mag;
   __result._y = this->_y/mag;
   __result._z = this->_z/mag;
 
   return;
}
对该函数调用的操作也需要修改, 于是
obj.normalize();
变成:
Point3d res;
normalize _ _ 7Point3dFv(&obj, res);
ptr ->normalize();
变成:
Point3d res;
normalize _ _ 7Point3dFv( ptr, res);
 
若未进行 NRV 优化,则
obj.normalize();
变成:
normalize _ _ 7Point3dFv(&obj);
ptr ->normalize();
变成:
normalize _ _ 7Point3dFv( ptr );
 
名称处理:
待整理
 
2 )虚成员函数
normalize() 是一个虚函数,则以下调用:
ptr ->normalize();
则转化成:
( * ptr->vptr[ 1 ])( ptr );
其中:
l         vptr 是编译器产生的指针,指向虚表( virtual table ),每个包含一个或多个虚函数的类对象都会包含一个 vptr
l         1 virtual table slot 的索引值,它关联 normalize () 函数
l         ( ptr ) 中的 (ptr 表示 this 指针
 
而对于以下调用
// Point3d obj;
obj.normalize();
则没有必要转化成
    
*obj.vptr[1] )( &obj
而应该转化成这样:
normalize _ _ 7Point3dFv(&obj); /* 即和非静态成员函数调用一样 */
因为经由一个类对象或类域操作符( :: )调用一个虚函数,这种操作总是像调用一般非静态成员函数一样。(只有通过引用或指针,虚函数才可能呈现多态性)
3 )静态成员函数
normalize() 是一个静态成员函数,则以下调用:
obj.normalize();
ptr->normalize();
则转化成:
// obj.normalize();
normalize__7Point3dSFv();
// ptr->normalize();
normalize__7Point3dSFv();
 
静态成员函数的主要特性是没有 this 指针,下面的次要特性都根源于此主要特性
1.       不能直接存取类中的非静态成员
2.       不能被声明为 const volatile virtual
3.       不需要(但可以)经由类对象才被调用,通常使用类::调用静态成员函数
 
若取一个 静态成员函数的地址,则就是其在内存中的地址,其地址的类型并不是一个“指向类成员函数的指针”,而是 一个“非成员函数指针”。
例如,对于静态成员函数
unsigned int 
    
Point3d:: object_count() 
    
{ 
    
   return _object_count; 
    
} 
    
会转换成:
unsigned int
object_count__5Point3dSFv()
{
   return _object_count__5Point3d;
}
&Point3d::object_count(); 
    
会得到一个数值,其类型是:
unsigned int (*)();
而不是
unsigned int ( Point3d::* )();
 
静态成员函数由于缺乏 this 指针,因此差不多等同于非成员函数。

你可能感兴趣的:(C++,读书笔记,职场,对象模型,休闲)