纯Swift类的函数调用原理,类似于C++的虚函数表
- 纯Swift类的函数调用,类似于C++的虚函数表,是编译时决议的。所以专门了解一下vtable还是有必要的。
- 一个含有虚函数的类,在创建对象时会额外增加一张虚表vtable,表中每一项记录了虚函数的入口地址,编译时对象会增加一个虚指针vpt(四个字节),指向虚函数表的起始位置,将对象和表关联起来。
2.下面举个栗子,含有虚函数的单继承多层次的类关系:
using namespace std;
class A {
protected:
int a1;
int a2;
public:
virtual void display() { std::cout<<"A:dis() \n"; }
virtual void clone() { std::cout<<"A:clone()"; }
};
class B: public A {
protected:
int b;
public:
virtual void display() { std::cout<<"B:dis() \n"; }
virtual void init() { cout<<"B:init()"; }
};
class C: public B {
protected:
int c;
public:
virtual void display() { std::cout<<"C:dis() \n";
A::display(); }
virtual void execute() { cout<<"C:execute"<<this->a1<<"\n"; }
};
int main(int argc, const char * argv[]) {
C *p = new C();
p->display();
p->execute();
return 0;
}
- 从上图可以知道,对于单继承,无论继承层次多深,都只是增加一个指针;基类中的虚函数在vtable中的索引是固定的,不会随着继承层次的增加而改变。例如,display()的索引值始终是0.
- 当调用函数时,利用指针vfptr间接转换,可得到虚函数的入口地址:
比如,调用p->display()
在编译器内进行转换,变成:((p->vptr)[0])(p);
其中,0是display()在vtable的索引值,(p->vptr)[0]是它的入口地址。
所以调用对象的不同虚函数,其实是访问虚函数表,通过改变不同的索引值,从而访问到函数的入口地址。