虚函数表和虚表指针
1,虚函数的含义
用virtual声明类的成员函数称之为虚函数
2,作用
用于实现多态
存在继承关系,子类继承父类
子类重写了父类的virtual function
子类以父类的指针或者引用的身份出现
3,虚函数的实现原理
其中的关键就是两点:
虚函数表指针
虚函数表
想要理解其中的结构,还是要从内存布局看起
下面都是用debug的方式,查看其中的布局大小
一定要理解下面的四段代码
1,普通类的内存布局
代码:
因为普通函数并不占内存,所以此时对象a的并不显示内存
2,虚函数类的内存布局
代码:
此时因为类内定义了虚函数,所以就有了一个虚函数指针:_vfptr
这个函数指针指向的即为虚函数表,表内是两个虚函数的地址
3,子类不重写父类虚函数的内存布局
代码:
此时子类继承了父类的虚函数,但是没有重写,因此子类的虚函数指针指向的表中依然存放的父类的虚函数,因此也可以看到,里面的地址都是一样的
4,子类重写父类虚函数的内存布局
代码:
这个局部变量中可以看到,重写的函数虚函数表中的地址变了,没重写的函数,在虚函数表中的地址依然没变。
上面四幅图看懂就大概知道了虚函数表指针和虚函数表的关系了。
即当类中有虚函数时,就会自动产生一个虚函数表指针,这个指针指向一个虚函数表,表中就存放类中定义的虚函数
总结:
1,虚函数表指针
什么是虚函数表指针,他在哪里,有什么用?
我们把对象首地址开始的4个字节或8个字节,这个位置我们称之为虚函数表指针(可以添加点属性看看位置)。它里面包含一个地址指向的就是虚函数表的地址
2,虚函数表
什么是虚函数表,他又在哪里,有什么用
虚函数表里面是一组地址的数组(函数指针数组),他所在的位置就是虚函数表指针里面所存储的地址,它里面所包含的地址就是我们重写了父类的虚函数的地址(没有重写父类的虚函数那么默认的就是父类的函数地址)
99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
每个包含了虚函数的类都包含一个虚表,虚表是属于类的,而不是属于某个具体的对象,一个类只需要一个虚表即可。同一个类的所有对象都使用同一个虚表 (非多重继承语境下)。为了指定对象的虚表,对象内部包含一个(唯一的)虚表的指针,来指向自己所使用的虚表。如果对比2个类,一个拥有虚函数而一个没有,那么前者的实例应该会比后者的实例多出一个指针的大小,下面验证一下:
88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
父类中如果存在虚函数,类中会隐含一个虚函数指针,指向一个虚函数表,表中存储了父类的所有虚函数入口地址。子类继承父类,会复制一份虚指针和虚表。此时如果子类重写了父类的虚函数,会将重写的虚函数入口地址更新到虚表中。若父类指针指向了子类,并且调用虚函数,会在我们更新的虚表里找虚函数入口地址,此时已经是子类重写的虚函数了。
77777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777
虚函数实现原理:
一个含有虚函数的类都有至少一个虚函数表,里面存放着该类所有虚函数的指针。
类的所有对象都共享类的虚函数表,这些对象都有一个或多个虚函数表指针来指向一个或多个虚函数表。
子类继承父类时也会继承父类的虚函数表,但父类的虚函数表和子类继承过来的虚函数表是两个独立的表。
当子类重写父类中虚函数的时候,会用重写后函数的指针代替继承过来的虚函数表中的原函数指针。
当使用父类类型的指针指向一个父类或子类对象,并调用其虚函数,会在程序运行时根据指向的对象是父类还是子类对象来决定调用的是父类中的虚函数还是子类重写的虚函数,称为动态多态。具体实现原理:当用父类指针指向子类对象时会进行指针转换,使得父类指针指向子类对象中的父类部分,通过指针调用指向对象的虚函数时,会通过指向对象中起始位置的虚函数表指针找到虚函数表,经过一定的偏移量找到对应的虚函数指针。
6666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666
在C++中,虚函数是一种特殊的函数,它可以被子类覆盖或继承。为了实现虚函数的动态绑定,C++编译器使用了虚函数表(Virtual Function Table,VTable)和虚函数表指针(Virtual Function Table Pointer,VPtr)。
虚函数表是一个存储着虚函数地址的表格,每个类都有一个唯一的虚函数表,其中包含该类的虚函数地址以及一些其他信息。每个对象都有一个指向其所属类的虚函数表的指针,这个指针称为虚函数表指针。当一个虚函数被调用时,程序会使用对象的虚函数表指针来查找该函数的地址,然后调用该函数。
在编译时,编译器会在每个类中添加一个虚函数表指针,用来指向类的虚函数表。当类被实例化时,对象会自动包含一个指向该类的虚函数表指针。当子类继承或覆盖了父类的虚函数时,子类会重新生成自己的虚函数表,并将其虚函数表指针指向新的虚函数表。
简单来说,虚函数表和虚函数表指针是C++实现虚函数动态绑定的重要机制。虚函数表保存了虚函数的地址,而虚函数表指针则指向类的虚函数表。当调用虚函数时,程序会使用对象的虚函数表指针来查找该函数的地址,然后调用该函数。
C++为了解决动多态引入了虚表和虚表指针。
什么是虚表?
对于每一个有虚函数的类,编译的时候都会生成一个虚表。注意,这个虚表是在编译的时候都已经生成。虚表里面一般有这个类对应的虚函数的地址,typeinfo地址等信息。
什么是虚表指针?
每个有虚函数的类的实例里面都有虚表指针。一个有虚函数的类的实例里面有一个或者多个虚表指针,数量取决于有多少个基类是有虚表的。
555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555