多态:不关心子类对象的具体类型,调用子类对象自己的虚函数。
实现:虚函数。通过父类的指针或引用调用虚函数时才发生动态绑定。
虚表:虚函数构成的数组。
虚表指针:指向数组的指针
虚表指针赋值时机:构造对象时,给本对象赋值为自己的虚表指针。子类构造时,先构造父类,于是先赋值为父类的虚表指针,父类构造完毕后,再赋值为子类的虚表指针。
虚表的创建在编译期就完成了,数据保存在exe中。
简单举例:
class B { public: B() { } virtual void f1() {} virtual void f2() {} virtual void f3() {} virtual void f4() {} }; class D :public B { public: D() { } virtual void f1() override {} virtual void f2() override {} };
010 Editor打开磁盘中的exe,选中的一行是虚表,有四个虚函数:
vs2019调试状态:
内存情况:
整个过程:
一.编译期:
1.编译器创建父类B的虚表
B::vftable[4] = {B::f1, B::f2, B::f3, B::f4};
2.编译器拷贝父类B的虚表给子类
D::vftable[4] = {B::f1, B::f2, B::f3, B::f4};
3.编译器替换子类D重写的部分,未重写则继续用父类的
D::vftable[4] = {D::f1, D::f2, B::f3, B::f4};
二.运行期:
书写B *p = new D;时,发生如下事情:
1.构造父类B
__vfptr = B::vftable;
2.构造子类D
__vfptr = D::vftable;
调用虚函数p->f1();时,通过虚表指针,以数组下标寻址的方式获取数组第一项,即虚函数f1的地址,然后调用:
(p->(__vfptr[0])) ();