跟着侯捷老师学C++☛对象模型

155 阅读2分钟

class对象里有什么

有三个Class,A为基类,B继承A,C继承B

class A {
public:
  virtual void vfunc1();
  virtual void vfunc2();
          void func1();
          void func2();
          
private:
  int m_data1, m_data2;
}

class B: public A {
public:
  virtual void vfunc1();
          void func2();
          
private:
  int m_data3;
}

class C: public B {
public:
  virtual void vfunc1();
          void func2();
          
private:
  int m_data1, m_data4;
}

image.png

一个类对象里面占有了什么东西,我们就看class内部的data数据。

  • A

对于A里面有m_data1m_data2

image.png

我们看到上面有一个vptr,是由于Aclass里,拥有了两个虚函数,父类有的虚函数,子类一定有。所以有一个指针vptr,专门来指向一个表vtbl,这个表里就保存了虚函数的地址

所以一个A的对象a,它的大小就是一个指针大小+2个int大小(4+4*2 = 12)

  • B 对于B,继承了A的data数据,也就是m_data1m_data2,自己还有m_data3。同样,因为A有虚函数(或者说自己也有虚函数),所以还有一个vptr。注意它的地址和A的vptr地址不一样。

image.png

我们看vtbl里面,发现其中一个值0x401F10是一样的,这是因为B只覆写了vfunc1,而从A那里继承下来了vfunc2

B的大小一个指针大小+3个int大小(4+4*3 = 16)

  • C 对于C,继承了B的数据m_data1m_data2m_data3。再加上自己的m_data1m_data4。注意这里的m_data1没有覆盖父类的。同样,也拥有一个vptr,里面有一个地址也是一样的(也就是vfunc2)。

image.png C的大小一个指针大小+3个int大小(4+4*5 = 24)

  • 函数 总共有8个函数,4个虚函数,4个非虚函数
    与vtbl联系起来: image.png

编译器如何动态调用

动态绑定三大条件:

  1. 通过指针调用
  2. 调用虚函数
  3. 指针向上转型

动态绑定链路:通过指针,找到vptr,再找到vtbl,再看里面的函数指向哪里,就调用什么函数

(*(p->vptr)[n])(p);

p就是对象指针,找到vptr指针调用,得到vtbl,然后通过虚函数名称就知道要去找vtbl中的第n个函数;然后通过*拿到这个函数的实际值,再传入p调用。这就是调用一个对象p的虚函数的链路。

B b;
A a = (A)b;
a.vfunc1();  // 由于没有通过指针调用虚函数,所以就是静态绑定,调用的就是a自己的vfunc1 -> a.vfunc1
A* pa = new B;
pa->vfunc1();  //  pa是指针,调用的是虚函数,且向上转型(new的是B,但是却赋给了AAB的父类,所以向上转型了)。满足动态绑定的三大条件,所以此时调用的是B::vfunc1()