成员函数
Virtual Member Functions
每个声明了虚函数或者继承了有虚函数的类,都会有一个自己的vtbl
。同时该类的每个对象都会包含一个vptr
去指向该vtbl
。虚函数按照其声明顺序放于 vtbl
表中, vtbl
数组中的每一个元素对应一个函数指针。如果子类覆盖了父类的虚函数,将被放到了虚表中原来父类虚函数的位置。
如果 normalize()
是一个 virtual member function
,那么调用:ptr->normalize();
实际上会被编译器转化为:(*ptr->vptr[1])(ptr);
vptr
是指向虚函数表的指针1
是表中该函数的索引,ptr
表示的是this
指针
显示地调用“虚函数”可以压制机制,比如说,point3d::normalized()
就不会触发虚拟机制,其行为和非静态成员函数行为一致,直接可以在编译时确定调用类的normalized()
。
注意:类的对象调用虚函数是不具有多态性质,只能调用对象本身的虚拟函数版本。
Point3d obj;
obj.normalize();
// 会被编译器转换为: *obj.vptr[1](&obj); 虽然正确,但是没有必要,相当于point3d::normalized(),
// 决议方式类似于非静态成员函数: normalize_7Point3dFv(&obj);
静态成员函数
静态成员函数被转换为非成员函数的方式:
obj.normalized(); // normalize 7Point3dSFv();
ptr->normalized(); // normalize 7Point3dSFv();
// 实际上应该是由类直接调用静态函数,而不是类对象或者类指针
对象调用和指针调用可见完全一样,而且转换之后,不带有任何关乎调用这个静态函数的类信息,这也就说明了为什么不能在静态函数中直接调用“普通类成员”。
静态成员函数的主要特性就是它没有this
指针,次要特性:
- 不能够直接存取所属类中的非静态成员函数
- 不能被声明为
const
、virtual
、volatile
。 - 不需要通过类对象来调用
如果获取一个静态成员函数的地址,获得的是这个静态成员函数在内存中的位置,也就是其地址。因为其没有this
指针,所以地址类型是“指向非成员函数指针”,而不是“指向类成员函数指针”。即:
&Point3d::object_count();
// 得到一个数值,其类型是 unsigned int (*)();
// 而不是 unsigned int (point3d::* )();
因此,缺乏this
指针的静态成员函数