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