深度探索C++对象模型<八>

Member的各种调用方式

Nonstatic Member Functions

由于C++的一个设计标准就是使得nonstatic member function要有和一般的nonmember function有相同的效率;

这就带来了一个变化,就是编译器内部会将nonstatic member function转换为等价的nonmember function实体;

转化步骤:

  • 改写函数的原型,安插一个新的参数,即this指针;
  • 将每一个对nontatic data member的存取操作改写为通过this指针的操作;
  • 将member function改写成一个外部函数,修改函数名称;

例如一个这样的函数:

1
2
3
4
5
6
7
8
Point3d Point3d::normalize() const
{
register float mag = magnitude();
Point3d normal;
normal.x = x/mag;
normal.y = y/mag;
normal.z = z/mag;
}

将可能会改写成:

1
2
3
4
5
6
void normalize_7Point3dFv(register const Point3d *const this, Point3d &__result)
{
register float mag = this->magnitude();
__result.Point3d::Point3d(this->x/mag, this->y/mag, this->z/mag);
return;
}

特别的,编译器内部会对名称进行mangling处理,虽然目前处理方式没有统一的标准,但一般来说会在member名称前面加上类名,甚至为了保证重载的操作,会加上函数参数的类型,当然如果声明了extern C,就会抑制了这种效果,这也是extern关键字的一个重要功能。

有时我们看到的编译器报错,显示了非常奇怪的函数名称报错,往往就是因为name mangling的原因。

Virtual member functions

如果normalize()是一个虚函数,那么以下调用会转化为:

1
2
ptr->normalize();
(*ptr->vptr[1])(ptr);

1就是virtual function slot的索引值,关联到normalize()函数。

而如果里面的magnitude()函数也是虚函数,那么由于normalize会先调用,决定了object的类型,所以编译器会使用更加明确的调用方式,而不是(*ptr->vptr[2])(ptr);例如:

1
register float mag = Point3d::magnitude();

Static Member Functions

如果normalize是一个静态函数,那么这两种调用会变为:

1
2
3
4
obj.normalize();
prt->normalize();
//会转为
normalize_7Point3dSFv();

由于static member function没有this指针,所以:

  • 不能直接存取其中class的nonstatic members;
  • 不能被声明为const, volatile或者virtual;
  • 不需要经由对象调用(但是如果static member是一个private,就很可能要依赖于对象);

如果要取一个static member function的地址,那么其类型会是:unsigned int (*)();