C++多态——虚函数表vtable

纯Swift类的函数调用原理,类似于C++的虚函数表

  • 纯Swift类的函数调用,类似于C++的虚函数表,是编译时决议的。所以专门了解一下vtable还是有必要的。
  1. 一个含有虚函数的类,在创建对象时会额外增加一张虚表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"<a1<<"\n"; }
};

int main(int argc, const char * argv[]) {

    C *p = new C();
    p->display();
    p->execute();
    
    return 0;
}
类和虚表关系图.png
  • 从上图可以知道,对于单继承,无论继承层次多深,都只是增加一个指针;基类中的虚函数在vtable中的索引是固定的,不会随着继承层次的增加而改变。例如,display()的索引值始终是0.
  • 当调用函数时,利用指针vfptr间接转换,可得到虚函数的入口地址:
    比如,调用p->display()
    在编译器内进行转换,变成:((p->vptr)[0])(p);
    其中,0是display()在vtable的索引值,
    (p->vptr)[0]是它的入口地址。
    所以调用对象的不同虚函数,其实是访问虚函数表,通过改变不同的索引值,从而访问到函数的入口地址

你可能感兴趣的:(C++多态——虚函数表vtable)