虚表

C成员函数

如果用C语言做一个类似面向对象的类

typedef struct Actress {
    int height;
    int weight;
    int age;
    
    void (*desc)(struct Actress*);
};

void profile(Actress* obj) {
    printf("height:%d weight:%d age:%d", obj->height, obj->weight, obj->age);
}

int main() {
    // 初始化
    Actress a;
    a.height = 168;
    a.weight = 50;
    a.age = 20;
    a.desc = profile;
    
    // 成员函数调用
    a.desc(&a);
    return 0;
}

想要达到面向对象中数据和操作封装在一起的效果,只能给struct里面添加函数指针,然后给函数指针赋值。然而函数指针是有空间成本的,这样写每个实例化的对象都会有一个指针大小(比如\(FSIZE=8B\)),如果要实例化\(N\)个对象,每个对象有\(M\)个成员函数,那么就要占用\(FSIZE\cdot N\cdot M\)的内存

C++成员函数

静态绑定

class Actress {
public:
    int height;
    int weight;
    int age;
    void desc() {
        printf("height:%d weight:%d age:%d", obj->height, obj->weight, obj->age);
    }
};

在C++中,类和操作的封装只是对于程序员而言的。而编译器编译之后,得到的还是面向过程的代码。编译器会帮成员函数增加一个额外的类指针参数,运行期间传入对象实际的指针。类的数据(成员变量)和操作(成员函数)其实还是分离的

在类不含有虚函数的情况下,编译器在编译期就会把函数地址确定下来,运行期间直接调用这个地址的函数即可。这种函数调用方式也就是所谓的静态绑定

动态绑定/延迟绑定

class Rect {
public:
    Rect(int h, int w): height(h), width(w) {}
    virtual void desc() {
        printf("Rect height:%d width:%d", height, width);
    }
    int height;
    int width;
};

class Square: public Rect {
public:
    Square(int a): Rect(a,a) {}
    virtual void desc() {
        printf("Square height:%d width:%d", height, width);
    }
};

int main() {
    Square s(10);
    s.desc();   // Square height:10 width: 10
    Rect* r1 = &s;
    Rect& r2 = s;
    r1->desc(); // Square height:10 width: 10
    r2.desc();  // Square height:10 width: 10
    return 0;
}

用父类指针/引用指向子类,最终调用desc函数还是调用子类的

这个现象称之为动态绑定/延迟绑定

NO VIRTUAL

倘若父类Rect中的desc函数前面的virtual去掉,Rect*/Rect&最终将调用父类的desc函数:虽然指针指向的还是子类的内存空间,但是类的数据(成员变量)和操作(成员函数)其实是分离的。仅从对象的内存布局来看,只能看到成员变量,看不到成员函数,调用哪个函数是编译期就固定了的,编译期只能识别父类的desc

虚表

C++具体多态的实现一般是编译器厂商自由发挥的,但使用虚表指针来实现多态几乎是最常见的做法(基本上已经是最好的多态实现方式)

含有虚函数的类编译期间,编译器会自动给这种类在起始位置追加一个虚表指针(称之为vptr)。vptr指向一个虚表(称之为vtable或vtbl),虚表中存储了实际函数地址

img

top offset

用于多继承中,在调用虚函数之前,对this指针进行调整,保证即使是Base*,也能调用到正确的Derive,这个技巧就是所谓的thunk

typeinfo

RTTI:运行阶段类型识别(Runtime Type Identification)