Function语意学
float Point3d::magnitude()const {
return sqrt(_x*_x + _y*_y + _z*_z);
}
Point3d Point3d::normalize()const {
/*
用register说明的局部变量称为寄存器变量,该变量将可能以寄存器作为存储空间。
register说明仅能建议(而非强制)系统使用寄存器,这是因为寄存器虽然存取速
度快,但个数有限,当寄存器不够用时,该变量仍按auto变量处理。
*/
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();时发生什么操作?
4.1Member的各种调用形式
Nonstatic Member Functions
nonstatic member function:非静态成员函数
nonmember function:非成员函数
C++的设计准则之一就是:nonstatic member function至少必须和一般的nonmember function有相同的效率。也就是说,下面两个函数:
float magnitude3d(const Point3d *_this){...}//非成员函数
float Point3d:magnitude3d()const{...}//非静态成员函数
在这两者中选择member function不应该带来什么额外的负担。这是因为编译器内部已将“member函数实例”转换为对等的“nonmember函数实例”。
比如:
下面是magnitude()的一个非成员函数的定义:
float magnitude3d(const Point3d *_this) {
return sqrt(_this->_x*_this->_x + _this->_y*_this->_y + _this->_z*_this->_z);
}
虽然看起来nonmember function相对而言没有效率,它是间接地经由参数取用坐标成员,而member function却是直接取用坐标成员。然而实际上member function被内部转化为nonmember的形式,下面是具体步骤:
1.改写函数的函数原型安插一个额外的参数到成员函数之中,用以提供一个存取管道(也就是与类Point3d建立联系),使class object得以将此函数进行调用。额外参数被称为this指针。
Point3d::Point3d::magnitude(Point3d *const this)
如果成员函数是const,则变成
Point3d::Point3d::magnitude(const Point3d *const this)
2.将每一个“对nonstatic data member”的存取操作改为经由this指针来存取:
return sqrt(_this->_x*_this->_x + _this->_y*_this->_y + _this->_z*_this->_z);
3.将member function重新写成一个外部函数,将函数名经过一定的处理(其实就是令取一个名字),使它在程序中成为独一无二的词汇:
extern magnitude__7Point3dFv(register Point3d *const this);
经过上述3步,成员函数转化完成,此时每一个调用操作也必须进行转换,于是:
obj.magnitude();变成了magnitude__7Point3dFv(&obj);
ptr->magnitude()变成了magnitude__7Point3dFv(ptr);
开始所提的normalize()函数会被转化为下面的形式:
void normalize__7Point3dFv(register const Point3d *const this,
Point3d &__result) {
register float mag = this->magnitude();
__result.Point3d::Point3d();//default constructor
__result._x = this-> _x / mag;
__result._x = this-> _x / mag;
__result._x = this-> _x / mag;
return;
}
一个比较有效率的做法是直接建构“normal”值,也就是匿名类对象
Point3d::Point3d::normalize()const {
register float mag = magnitude();
return Point3d(_x / mag, _y / mag, _z / mag);
}
它会被转为下面的代码:
void normalize__7Point3dFv(register const Point3d *const this, Point3d &__result) {
//调用拷贝构造函数
__result.Point3d::Point3d(this->_x / mag, this->_y / mag, this->_z / mag);
return;
}
名称的特殊处理
一般而言,member的名称前面会被加上class名称,形成独一无二的命名,比如:
class Bar{Public:int ival;}
其中的ival可能会经过编译器的处理变成ival__3Bar,这样做的目的是因为在派生操作中派生类对象内部结合了base class和derived class两者:
class Foo {
public:
int ival__3Bar;
int ival__3Foo;
};
这样的话,不管处理哪一个ival,都可以绝对清楚地指出来。但是,如果在一个类中的重载函数要经过怎样的名称处理才能区分呢?比如:
class Point {
public:
void x(float newX);
float x();
};
如果按照前面的方法就是转换为:
class Point {
public:
void x__5Point(float newX);
float x__5Point();
};
这会导致两个被重载化的函数实例拥有相同的名称。为了让它们独一无二,就是把参数类型也编码到函数名中,这样就一定可以制造出独一无二的结果。
class Point {
public:
void x__5PointFf(float newX);
float x__5PointFv();
};
这样的好处就是两个实例如果拥有独一无二的name mangling(函数名称),那么任何不正确的调用操作在链接时期就无法通过。
Virtual Member Functions
如果normalize()是一个virtual member function,那么以下的调用:
ptr->normalize();
将会被内部转化为:
(* ptr->vptr[1])(ptr);
注意:
1.vptr表示由编译器产生的指针,指向virtual table。它被安插在每一个声明有(或继承自)一个或多个virtual functions的class object中。事实上,其名称也会被调整,因为在一个复杂的class派生体系中,可能存在多个vptrs。
2.1是virtual table slot的索引值,关联到normalize()函数。
3.第二个ptr表示this指针,前面说过的,成员函数会被处理成非成员函数,然后传入一个参数。
类似的道理&