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;
}
一个类对象里面占有了什么东西,我们就看class内部的data数据。
- A
对于A里面有m_data1和m_data2。
我们看到上面有一个vptr,是由于Aclass里,拥有了两个虚函数,父类有的虚函数,子类一定有。所以有一个指针vptr,专门来指向一个表vtbl,这个表里就保存了虚函数的地址
所以一个A的对象a,它的大小就是一个指针大小+2个int大小(4+4*2 = 12)
- B
对于B,继承了A的data数据,也就是
m_data1和m_data2,自己还有m_data3。同样,因为A有虚函数(或者说自己也有虚函数),所以还有一个vptr。注意它的地址和A的vptr地址不一样。
我们看vtbl里面,发现其中一个值0x401F10是一样的,这是因为B只覆写了vfunc1,而从A那里继承下来了vfunc2
B的大小一个指针大小+3个int大小(4+4*3 = 16)
- C
对于C,继承了B的数据
m_data1和m_data2、m_data3。再加上自己的m_data1和m_data4。注意这里的m_data1没有覆盖父类的。同样,也拥有一个vptr,里面有一个地址也是一样的(也就是vfunc2)。
C的大小一个指针大小+3个int大小(4+4*5 = 24)
- 函数
总共有8个函数,4个虚函数,4个非虚函数
与vtbl联系起来:
编译器如何动态调用
动态绑定三大条件:
- 通过指针调用
- 调用虚函数
- 指针向上转型
动态绑定链路:通过指针,找到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,但是却赋给了A,A是B的父类,所以向上转型了)。满足动态绑定的三大条件,所以此时调用的是B::vfunc1()