- C++中类的非静态成员函数和一般函数具有相同的效率,编译器内部会将成员函数转化为对等的普通函数实体,比如加上 this 指针。
- 一般而言,编译器对函数会用 ”mangling“ 手法进行命名处理,这也是函数重载的支持手段。
- 独立于 class object 之外的存取操作,在某个时候特别重要。比如说 static 成员函数,它的主要特性是没有 this 指针,因此差不多等同于非成员函数,所以它可以成为 callback 函数,可以成功地应用在线程(threads)函数上(见识过了)。
- 直接使用基类指针指向使用 new 分配的派生类对象,如: point ptr = new point3d;这种多态在编译期可以完成,virtual base class 的情况除外。这种多态称为消极多态。而当被指出的对象是编译器才能决定的,就成为积极多态了。如:ptr->z(),执行期传指针给 ptr,确定 ptr 的真正类型。
- 一个 class 中 vtbl 含有所有的活跃虚函数实体的地址,包括:(1)这个 class 锁定地的虚函数实体,它会 overriding 一个可能存在的基类虚函数实体。(2)继承自基类的函数实体,这是在派生类决定不 overriding 时才会出现的情况。(3)一个 pure_virtual_called() 函数实体,它既可以扮演 pure virtual function 的空间保卫者角色,也可以当作执行器异常处理函数(有时会用到)。(最后一点暂时不太懂)
- 在多重继承中支持虚函数,其复杂度围绕在第二个或后继基类身上,以及 ”必须调整 this 指针” 这一点。
- 多重继承中调整指针(注意没说虚函数):比如对于 base *pbase = new derived(没有多态),新的 derived 对象必须调整this 指针,以指向 base subobject。如果没有这样的调整,指针的任何非多态运用都将失败,像 base->data_base,存取操作都会失败!或者 delete base,这个操作执行时又必须把 this 指针指到头部,因为要析构的大小是 derived 对象大小。这些操作必须在执行期完成,也就是说 this 指针跳跃的这些 offset 必须知道,其实会存于 vtbl 之中。如果 base 是派生类的最基类,那么 this 指针无需调整,因为它们起始位置是一样的。
- 有三种情况,第二或后继的基类会影响对虚函数的支持:(1)通过一个指向第二基类的指针,调用派生类虚函数(指针必须后移)。(2)通过一个派生类的指针,调用第二个基类中一个继承而来的 virtual function。此情况下,派生类指针必须再次调整,以指向第二个基类子对象。(3)第三种情况发生于一个语言扩充性质之下,允许一个 虚函数返回值类型发生变化,可能是基类类型,也可能是共有派生类类型。比如 base *pb = pb->clone(),该函数返回值为 derived*类型,所以在此处必须调整 offset。
附录:
成员函数指针获取及测试是否支持多态,以及&point::x取成员函数地址偏移量操作(取出的不是地址)测试:
class point {
public:
virtual float z() { return 0.0; };
};
class point3d : public point {
public:
point3d(float z) : z_(z) { }
virtual float z() { return z_; };
private:
float z_;
};
int main()
{
float (point::*pmf)() = &point::z;
point *ptr = new point3d(5);
float res = (ptr->*pmf)(); //测试是否同 ptr->z() 一样支持多态。
std::cout<<res<<std::endl; //输出 5,证明支持
std::cout<<&point::z<<std::endl; //输出函数地址偏移量:1
return 0;
}
输出结果: