运行时的多态性
运行时的多态是指在程序执行以前,无法根据函数名和参数来确定该调用哪个函数,必须在程序的执行过程中,根据具体情况来动态确定的,通过类的继承关系public(派生类对象可以自然地转换为基类指针或引用)和虚函数来实现的(晚绑定)
在 C++ 里,虚函数主要是借助虚函数表(简称虚表)和虚表指针来实现运行时多态的。下面为你详细解释其实现机制。在程序运行时才确定函数调用的具体目标,在对象创建时,生成虚表指针(记录虚函数表地址),根据对象指针或引用指向的实际对象,从虚函数表中选择函数调用
虚表
虚函数指针表(虚表):编译器为每个包含虚函数的类生成的一个指针数组,用于存储该类中所有虚函数的地址。如果派生类重写了基类的虚函数,那么在派生类的虚函数表中,对应位置的函数指针会被替换为派生类重写后的函数地址。每个类都有自己独立的虚函数表,即使派生类没有重写基类的虚函数,它的虚函数表中也会包含从基类继承过来的虚函数地址。这个数组的首元素之上存储RTTI(运行时类型识别信息的指针),从数组下标0开始依次存储虚函数地址,最后一个放nullptr。
虚表指针
虚表指针:每个对象内部都包含一个指向其所属类的虚函数表的指针。在对象构造时,虚表指针会被初始化,指向该对象所属类的虚函数表,并且在对象的整个生命周期中保持不变。
需要注意的是:
- 虚函数在代码段,虚表在只读数据段(常量数据区),虚表指针与对象储存的位置相同在堆或栈。
- 虚表在编译时创建,虚表指针是在对象构造时创建。
- 虚表是类级别的结构,一个类无论多少虚函数,只对应一张表。
- 虚表在编译时生成,运行时通过 `vptr` 直接访问。
- 子类虚表仅替换被重写的函数地址,未被重写的函数仍指向父类实现。