4.1 Member 的各种调用方式

Nonstatic Member Functions 非静态成员函数

C++的设计准则之一就是: nonstatic member function 至少和一般的 nonmember function 有相同的效率。

float magnitude3d(const Point3d *_this){ ... }
float Point3d::magnitude3d() const { ... }

那么选择 member function 没有什么额外负担,因为编译器内部已将 member 函数实体转换为对等的 nonmember 函数实体。
下面是 magnitude() 的一个 nonmember 实例:

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. 改写函数的 signature (函数原型): 安插一个额外的参数到 member fucntion 中,来提供一个存取管道,使 class object 得以调用该函数。该额外函数被称为 this 指针:
Point3d Point3d::magnitude(Point3d *const this)	// non-const nonstatic member 的扩展
//如果 member function 是 const
Point3d Point3d::magnitude(const Point3d *const this)	// const nonstatic member 的扩展
2. 将每一个对 nonstatic data member 的存取操作改为经由 this 指针来存取:
{
    return sqrt(_this->_x *this->_x + _this->_y *this->_y + _this->_z *this->_z);
}
3. 将 member fucntion 重新写成一个外部函数,对函数名称进行 mangling 处理,使它成为程序中独一无二的语汇:
extern magnitude__7Point3dFv(register Point3d *const this);

每个调用操作也需要转换:

obj.magnitude();	// 转换为:mgnitude__7Point3dFv( &obj );
ptr->magnitude();	// 转换为:magnitude__7Point3dFv(ptr);


Point3d Point3d::normalize() const
{
    register float mag = magnitude();
    Point3d normal;

    normal._x = _x / mag;
    normal._y = _y / mag;
    normal._z = _z / mag;
    return normal;
}
// 内部转化为:(NRV优化)
void normalize__7Point3dFv(register const Point3d *const this, Point3d &__result)
{
    register float mag = this->magnitude();
    //default constructor
    __result._x = this->_x / mag;
    __result._y = this->_y / mag;
    __result._z = this->_z / mag;
    return;
}
//另一个比较有效率的方法
Point Point3d::normalize() const
{
    register float mag = magnitude();
    return Point3d( _x/mag, _y / mag, _z / mag);
}
//这个方法会被转化为:
void normalize__7Point3dFv( register const Point *const this, Point3d &__result)
{
    register float mag = this->magnitude();
    __result .Point3d::Point3d(_x/mag, _y / mag, _z / mag );
    return;
}

名称的特殊处理

mangle 的意思是 vt.乱砍, 损坏; n. 碾压机。 这意味着 name mangling 就是要先把你精心想出的名字们碾碎, 再拼成独一无二的样子。
一般而言, member 的名称前会被加上 class 名称, 形成独一无二的命名, 例如:

class Bar(){ public: int ival; };
ival__3Bar	//member 经过 name-mangling 后的结果之一,ival 可能变成这样

编译器你为何要这样? 请考虑这样的派生操作:

class Foo: public Bar{ public: int ival; };
//所以必须 name-mangling 为:
class Foo
{
public:
    int ival__3Bar;
    int ival__3Foo;
};

而对于函数,因为 member function 可以被重载化, 所以需要更广泛的 magling 手法:

class Point
{
public:
    void x(float newX);
    float x();
};
//转化为
class Point
{
public:
    void x__5Point(float newX);
    float x__5Point();
};

函数如果这么转化, 会导致被重载化的函数实体拥有相同的名称,为了让它们独一无二,再加上它们的参数链表(可以从参数原型得到)。(但如果你声明 extern "C" , 就会压抑 nonmember functions的 mangling 效果):

class Point
{
public:
    void x__5PointFf(float newX);
    float x__5PointFv();
};

虚函数成员函数

如果 normalize() 是一个 virtual member function, 那么以下的调用:

ptr->normalize();
被内部转化为:
(*ptr->vptr[1])(ptr);

vptr 表示由编译器生成的指针, 指向 virtual table。 事实上其名称也会被 mangled, 因为在一个复杂的 class 派生体系中,可能存在多个 vptrs。
1 是 virtual table slot 的索引值, 关联到 nirmalize 函数。
第二个 ptr 表示 this 指针。
类似的道理, 如果 magnitude() 也是一个 virtual function,它在 normalize() 中的调用操作将被转化如下:

//register float mag = magnitude();
register float mag = (*this->vptr[2] )(this);

此时,由于 Point3d::magnitude() 是在 Point3d::normalize() 中被调用, 而后者已经由虚拟机制而决议妥当,所以明确地调用 Point3d 实体会比较有效率, 并因此压制由于虚拟机制而产生的不必要的重复调用操作:

//明确的调用操作(explicit invocation) 会压制虚拟机制
register float mag = Point3d::magnitude();

如果 magnitude() 声明为 inline 函数会更有效率。使用 class scope operator 明确调用一个 virtual function, 其决议方式和 nonstatic member function 一样:

register float mag = magnitude__7Point3dFv(this);

一般虚函数的调用都是由指针或者引用,确定的对象不支持多态。(p24页)

Point3d obj;
obj.normalize();
// 不会转化为:
(*obj.vptr[1])(&obj);
因为经由 obj 调用的函数实体只可以是 Point3d::normalize() 经由一个 class object 调用一个 virtual function,这种操作总是被编译器像对待一般的 nonstatic member function 一样的决议:
normalize__7Point3dFv(&obj);	// 因为 normalize() 的归属无可争议

静态成员函数

如果 Point3d::normalize() 是一个 staitc member function, 以下两个操作:

obj.normalize();
ptr->normalize();

将被转为一般的 nonmember 函数调用, 像这样:

obj.normalize();	// normalize__7Point3dFv();
ptr->normalize();	// ptr->normalize__7Point3dFv();

………………………………没啥意思………………………………

posted @ 2021-04-20 11:49  点|滴  阅读(8)  评论(0)    收藏  举报