目录
1 Member的各种调用方式
1.1 非静态成员函数
C++的设计准则之一:nonstatic member function
至少必须和一般的nonmember function
有相同的效率。
//Point3d::magnitude()
float Point3d::magnitude() const {
return sqrt(_x * _x + _y * _y + _z * _z);
}
//magnitude()的nonmember定义
float magnitude3d(const Point3d * this) {
return sqrt(this->_x * this->_x +
this->_y * this->_y +
this->_z * this->_z);
}
编译器内部对待member函数实例会将其转换为对等的nonmember函数实例,转化步骤如下:
-
改写函数原型以安插额外参数this指针到
member function
中,用以提供一个存取通道,使类对象得以将此函数调用 -
将每一个对非静态数据成员的存取操作改为经由this指针来存取
-
将成员函数重写成一个外部函数,将函数名称经过“mangling”处理,使其独一无二
名称的特殊处理name mangling:
-
member名称前面会被加上class名称,形成独一无二的命名
-
对于重载的成员函数还可参考函数原型将其参数类型编码进名称。
将参数和函数名称编码在一起,可使编译器在不同的被编译模块之间达成一种有限的类型检验
1.2 虚拟成员函数
Point3d obj;
Point3d *ptr = &obj;
//若normalize()是一个virtual member function,那么以下的调用:
ptr->normalize();
//将会被内部转化为:
(*ptr->vptr[1])(ptr);
其中:
- vptr表明由编译器产生的指针,指向virtual table。它被安插在每一个“声明有(或继承自)一个或多个virtual functions"的类对象中
- 1 是vtable slot的索引值,关联到normalize()
- 第二个ptr表示this指针
//如果magnitude也是一个虚函数,则它在normalize()中的调用操作将被转换为:
//register float mag = magnitude();
register float mag = (*this->vptr[2])(this);
由于Point3d::magnitude()是在Point3d::normalize()中被调用的,而后者已经由虚拟机制而决议妥当,所以显式地调用"Point3d实例"会比较有效率,并因此压制由于虚拟机制而产生的不必要的重复调用操作:
register float mag = Point3d::magnitude();
如果magnitude()声明为inline函数,会更有效率。使用域操作符(::)显式调用一个虚函数,其决议方式会和非静态成员函数一样:
register float mag = magnitude——7Point3dFv(this);
对于经由不支持多态的对象的调用,即经由一个类对象调用一个虚函数,其操作应该总是被编译器像对待一般非静态成员函数一样进行决议:
//Point3d obj;
obj.normalize();
//(*obj.vptr[1])(&obj); 语意正确,但无必要
normalize_7Point3dFv(&obj);
1.3 静态成员函数
静态成员函数的调用操作会被转换为一般的nonmember函数调用
//如果Point3d::normalize()是一个static member function
//obj.normalize()
normalize__7Point3dSFv();
//ptr->normalize()
normalize__7Point3dSFv();
引入静态成员函数的原因:存取静态数据成员
Static member function的主要特性:没有this指针
次要特性:
- 不能够存取类中的非静态成员
- 不能够声明为const,volatile,virtual
- 不需要经由类对象才被调用
如果取一个静态成员函数的地址,获得的是其在内存中的位置,也就是其地址。由于static member function没有this指针,所以其地址的类型并不是一个“指向class member function的指针”而是一个“nonmember函数指针”
2 虚拟成员函数
virtual function的实现模型:每一个class有一个virtual table,内含该class之中有作用的virtual function地址,然后每个object有一个vptr,指向virtual table所在。
C++中的多态:以一个public base class的指针(或reference),寻址出一个derived class object
“消极多态”:扮演输送机制,可在编译时期完成
“积极多态”:指出的对象被真正使用
为支持虚函数机制,必须首先能够对于多态对象有某种形式的“执行期类型判断法(runtime type resolution)”,也就是在支持某种形式的“执行期多态(runtime polymorphism)”时对class提供额外的执行期信息。
识别class是否支持多态,唯一适当的方法即看它是否有任何virtual function。只要class拥有一个virtual class,它就需要这份额外的执行期信息
额外信息的内容:(若有此调用 ptr->z()
,z()
为虚函数)
- ptr所指对象的真实类型
- z()实例的位置
在实现上,在每个多态的类对象身上增加了两个member:
- 一个字符串或数字,表示class的类型
- 一个指针,指向某表格,表格中持有程序的虚函数的执行期地址
如何找到函数地址:
- 类对象中插入由编译器内部产生的指针,指向该表格
- 为找到函数地址,每个虚函数被指派一个表格索引值
vtable所含的active virtual function的类型:
- 重写的基类虚函数实例
- 继承自基类的虚函数实例
- 纯虚函数实例
2.1 单一继承下的虚函数
2.2 多重继承下的虚函数
2.3 虚拟继承下的虚函数
对于虚拟继承而言,不论是基类还是派生类都需要有一个指针来维护自己的虚表
3 指向成员函数的指针
//声明指向成员函数的指针
double (Point::*pmf)();
//声明并定义
double (Point::*coord)() = &Point::x;
coord = &Point::y;
非静态成员函数:若该函数非虚函数,则取其地址可得到它在内存中的真正地址
虚函数:对virtual member function取地址,所能获得的是在vtable中的索引值
编译器会根据不同的class特性提供多种指向成员函数的指针形式
4 内联函数
- 在类内定义的函数,默认内联
- 关键词inline只是一项请求,编译器会根据其执行成本作合理的扩展
- 对于形参的处理并非简单的用实参去替代,而是会根据其性质作合理操作
- 局部变量也不能直接复制,而是要进行mangling操作,防止和程序中的变量重名。
- 局部变量和副作用的参数会导致大量临时对象的产生