C++支持三种类型的成员函数:static、nonstatic和virtual,每一种类型被调用的方式都不相同。
设计准则 C++的设计准则之一就是:非静态成员函数至少和一般的非成员函数有相同的效率。
成员函数转化为非成员函数
转化步骤 1.改写函数原型,安插this指针到成员函数中(this指针指向对象数据的首地址)
//non-const nonstatic member函数的扩张过程 Point3d Point3d::magnitude(Point3d* const this) //如果member function是const,则变成: Point3d Point3d::magnitude(const Point3d* const this)2.将“对非静态数据成员的存取操作”改为经由this指针来存取:
{ return sqrt(_this->x * _this->_x + _this->y * _this->y + _this->z * _this->z); }3.将成员函数重新写成一个外部函数,将函数名称经过“mangling(名称改写)”处理,使它在程序中成为独一无二的词汇。
extern magnitude_7Point3dFv(register Point3d* const this);函数的调用转化为:
obj.magnitude(); //经编译器转换 magnitude_7Point3dFv(&obj); ptr->magnitude(); //经编译器转换 magnitude_7Point3dFv(ptr);转化结果
float ClassA::func(){ return _x + _y; } //被改写成类似这样 float func_ClassA(ClassA *this){//被修改的函数名在不同编译器中有不同策略,不过对编程来说影响不大,想要了解命名策略可以上网查找。 return this->_x + this->_y; }名称的特殊处理 成员名加类名,如果是函数再加上参数类型的编码(区别重载函数),可以形成独一无二的名称。
转化
ptr->normalize(); //内部转化 (*ptr->vptr[1])(ptr);●vptr表示指向虚表的指针 ●1代表次函数在虚表中的索引值 ●ptr表示this指针
显示类名调用/对象调用 这两种调用都用不到virtual机制,所以效率比较高
主要特性 没有this指针,所以: ●不能存取非静态成员 ●不能被声明为const、volatile或virtual ●不需要经过对象进行调用,不过大部分时候还是通过对象调用
无对象时的调用 ●((point3d*) 0)->object_count();将0转换为该类对象的指针,提供this指针来调用函数。
取静态成员函数地址 获得的是在内存中的位置,也就是地址,由于静态成员函数没有this指针,所以地址的类型并不是一个“指向类成员函数的指针”,而是一个“非成员函数指针”。
&Point3d::object_count(); 会得到 unsigned int(*)(); 而不是 unsigned int (Point3d::*)();静态成员函数的用途 没有this指针,差不多等同于非成员函数。所以可以成为一个callback函数,可以应用在线程方面。
pthread_create的函数原型中第三个参数的类型为函数指针,指向的线程处理函数参数类型为(void*),若线程函数为类成员函数,则this指针会作为默认的参数被传进函数中,从而和线程函数参数(void*)不能匹配,不能通过编译。静态成员函数就没有这个问题,里面没有this指针。
调用方法 下面的操作需要获得ptr在执行期的某些相关信息。
ptr->z();效率方面 真正需要执行期类型判断时这份信息就存在,不需要就不存在,不然一直都存在的话会造成效率低下。
解决方法 ●需要知道ptr所指对象的真实类型,这可使我们选择正确的z()实例。------>一串字符或数字,表示class的类型 ●z()实例的位置,以便可以调用------->一个指针指向某表格,表格中持有程序的虚函数的执行期地址。 为了找到表格,每一个类对象中安插一个编译器内部产生的指针(vptr),指向表格。 为了找到函数地址,每一个虚函数指派一个表格索引值。
执行期只需要在特定的虚函数表槽(记录执行期地址)中激活虚函数。 编译器转化 如何在编译时期设定虚函数的调用? ●调用z()时,不知道ptr所指对象的真正类型,但是知道经由ptr可以存取到该对象的虚函数表 ●不知道哪一个z()会被调用,但是知道每一个z()函数地址都在slot4中。
唯一在执行期才知道的是:slot4指向的是哪个z()函数?
问题: 如何调整 见书
建议不要在一个virtual base class中声明非静态成员变量,否则会越来越复杂。
非成员、静态成员和非静态成员函数都被转化为完全相同的形式,所以三者的效率完全相同
●非静态成员变量:成员在class布局中的bytes位置 ●非静态成员函数:如果是非虚函数,得到的结果是在内存中真正的地址,但是这个地址也不完全,需要被绑定于某个对象身上,才能够通过它调用该函数。所有的非静态成员函数都需要对象的地址(this指出) ●虚成员函数:在虚表中的索引值,因为地址在编译时期是未知的。
作用 对于封装提供了一种必要的支持,可以有效存取封装于class中的nonpublic数据。 是#define的安全替代品
缺点 如果inline程序被调用太多次,会产生大量的扩展码,使程序大小暴涨。 参数带有副作用,以一个单一表达式作多重调用,或者在inline函数中有多个局部变量,都会产生临时性对象,会更复杂。
inline int min(int i, int j){ return i < j ? i : j; } 这样调用时:会产生临时变量 minval = min(foo(), bar() + 1); 变为: int t1, t2; minval = (t1 = foo()), (t2 = bar() + 1), t1 < t2 ? t1 :t2; inline int min(int i, int j){ int minval = i < j ? i : j; return minval; } 这样调用,会产生局部变量: minval = min(val1, val2); 变为: int _min_lv_minval; minval = (_min_lv_minval = val1 < val2 ? val1 : val2), _min_lv_minval; }