Function 语意学

本文详细探讨了C++中成员函数的不同调用形式,包括非静态成员函数、虚拟成员函数和静态成员函数。非静态成员函数通过`this`指针转换为非成员函数形式,虚拟成员函数在多态调用中涉及虚表和指针调整,静态成员函数则不具备`this`指针且可直接作为非成员函数调用。文章还介绍了指向成员函数的指针,包括对虚函数的处理和在多重继承下的应用。最后,讨论了内联函数及其在参数和局部变量处理上的特点。
摘要由CSDN通过智能技术生成

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指针,前面说过的,成员函数会被处理成非成员函数,然后传入一个参数。

类似的道理&

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值