本章由下面问题引出:
当存在如下代码:
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指针,因此差不多等同于非成员函数。