第四章: Function语意学
Nonstatic Member Functions(非静态成员函数)
Point3d obj;
Point3d *ptr = &obj;
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::magnitude() const {
return sqrt(_x*_x+_y*_y+_z*_z);
}
C++的设计准则之一就是:nonstaticmember function至少必须和一般的nonmember function有相同的效率。这是因为编译器内部已将“member函数实例”转换为对等的“nonmember函数实例”。
举个例子,下面是magnitude()的一个nonmember定义:
float magnitude3d(constPoint3d *_this)const {
return sqrt(_this->_x *_this->_x +
_this->_y*_this->_y +
_this->_z*_this->_z );
}
而我们的member function版本将按如下步骤转换:
1. 改写函数的signature以安插一个额外参数(this指针)到member function中,结果如下:
Point3d Point3d::magnitude(const Point3d * constthis)
之所以有两个const,是因为第一个const表示magnitude函数具有const性质,第二个const表示this是const指针。
2. 将每一个对nonstatic data member的存取操作改为由this指针存取,结果如下
{
return sqrt(this->_x*this->_x +
this->_y *this->_y+
this->_z *this->_z);
}
3. 将member function重新写成一个外部函数,并将函数名称经过magling(所谓的Name Mangling是指在member的名称上加上class名称以及member function的signature)处理,使它成为在程序中独一无二的语汇:
extern magnitude_7Point3dFv(
register Point3d* constthis);
现在这个函数已经村换号,而其每一个调用也都必须转换,如下:
obj. magnitude ()变为magnitude_7Point3dFv(&obj);
ptr-> magnitude()变为 magnitude_7Point3dFv(ptr);
normalize()函数将会被转换为一下形式,其中假设NRV可行:
void normalize_7Point3dFv(registerconst Point3d*constthis, Point3d *_result){
register float mag = this->magnitude();
_result.Point3d::Point3d();
_result._x = this->_x/mag;
_result._y = this->_y/mag;
_result._z = this->_z/mag;
return;
}
Virtue Member Function(虚拟成员函数)
如果normalize()是一个虚拟成员函数,那么下面的调用
ptr->normalize();
将会被内部转化为
( * ptr->vptr[ 1 ])( ptr );
其中:
Vptr表示右边一起产生的指针,指向virtual table。
1是virtual table slot的索引值,关联到normalize();
第二个ptr表示this指针。
但对于以下调用:
Obj.normalize();
上述调用的函数实例只可以是Point3d::normalize(),所以编译器会将它转化如下:
Normalize_7Point3dFv( &obj );
像上面经由一个class object调用一个虚函数,这种操作应该总是被编译器对待一般的nonstaticmember function一样加以决议。
Static Member Function(静态成员函数)
如果Point3d::normalize()是一个static memberfunction,一下两个调用:
obj.normalize();
ptr->normalize();
将被转换为一般的nonmember函数调用,像这样:
normalize_7Point3dSFv();
normalize_7Point3dSFv();
独立于class object之外的存取class object的static data member操作主要有两种方法:如下
1. 程序上的解决之道是将0强制转换为一个class指针,因而提供一个this指针实例,如下面这个例子:
class Point3d{
public:
int object_count(){
cout<< x << endl;
return x;
}
private:
static intconst x = 5;
};
int main(){
((Point3d*)0 )->object_count();
return 0;
}
结果输出5.
2. 语言层面上的解决之道,引入static member function。Static member function的主要特性就是它没有this指针。以下次要特性统统根源于主要特性:
1) 他不能够直接存取其class中的nonstaticmembers
2) 他不能够被声明为const、volatile或virtual
3) 它不需要经由class object才被调用---虽然大部分时候它是这样被调用的
取一个static member function的地址,获得的将是其在内存中的位置,也就是其地址。由于static member function没有this指针,所以其地址的类型是一个nonmember函数指针,并不是指向class member function的指针。
Static member function由于缺乏this指针,因此差不多等同于nonmember function。
4.2 virtual member functions虚拟成员函数
Virtual function的一般实现模型(无继承):每一个class有一个virtual table,内含class之中有作用的virtual function的地址,然后每个object有一个vptr,指向virtual table的存在。这一节会讨论单一继承,多重继承和虚拟继承这三种情况。
单一继承:
在编译时期:会准备好虚函数的地址值存放在虚函数表中,并且表中的这组地址是固定不变的,表本身的大小也不会改变;为了找到表格会为每个class object安插一个指针指向该表格。
执行期要做的是在特定的virtual table slot中激活virtual function。
在单一