1.如果类中包含虚函数,用户没有显式给出构造函数编译器会给该类生成一份默认的构造函数。
2.如果类中包含虚函数,类对象模型会都出4个字节
3.对象前4个字节中的内容是在构造函数中编译器填充的
4.对象的前4个字节中内容,实际上是一个指针
5.对象前4个字节的指针指向了块内存空间,内存空间存放的是虚函数的入口地址
class Base { public: virtual void Func1() { cout << "Func1()" << endl; } private: int _b = 1; };
在运行后b对象的大小为8字节,除了_b成员,还多了一个_vfptr放在对象的前面,对象中的这个指针我们叫做虚函数表指针,一个含有虚函数的类中至少都有一个虚函数表指针,虚函数表存放的是虚函数的入口地址,虚函数表也称虚表。
在虚表中存放的是虚函数的入口地址,入口地址可以理解为指针,指针的类型为函数指针,void(*)()
基类虚表的构成规则:将虚函数按照类中声明的先后次序,将入口地址放入虚表中
派生类虚表的构建过程:
1,将基类虚表中的内容拷贝一份放到派生类的虚表当中
2.如果派生类重写了基类中的虚函数,则会用派生类中的虚函数的地址覆盖(替换)派生类虚表中相同偏移量的基类虚函数
3.派生类自己新增的虚函数按照在类中声明先后次序增加到派生类虚表最后
虚函数存放在哪? 虚表存在哪?
虚函数和普通函数一样都存放在代码段中,只是它的指针存到了虚表中,虚表中存放的是虚函数指针,不是虚函数。对象中存的不是虚表,存的是虚表指针,虚表存放在代码段的。
虚函数的调用过程:
代码需要先编译:在编译阶段如果编译器检测出函数不是虚函数则直接调用,如果是虚函数,但是没有满足多态的实现条件,则直接调用基类的虚函数,如果是虚函数,满足多态的条件,从虚表中找虚函数进行调用。
从虚表中找到虚函数进行调用(多态的原理)
1.从对象的前4个字节中取出虚表地址
2.传递this指针
3.从虚表中相应偏移量取到对应的虚函数
4.对该虚函数进行调用
满足多态以后的函数调用,不是在编译时确定的,是运行的起来以后到对象中取的,不满足多态的函数调用是编译时确定好的d
带有虚函数的多继承:
派生类中新增的虚函数,将该虚函数放在第一个虚表的的最后