虚函数表和虚函数表指针的创建时机。
背景:用来实现多态(包括静态多态和动态多态),多态的原理就是虚函数表和虚函数表指针
虚函数表的创建时机:
a.什么时候生成的?编译器编译的时候声生成的,(前提)编译的过程中发现virtual关键字修饰的函数(当然如果有继承且基类有virtual修饰的函数,那也有自己的虚函数表指针)
b.存在哪里?可执行程序(磁盘)、运行状态(内存)
磁盘中:
.bss:未初始化的或初始化为0的全局、静态变量
.data:初始化的静态或全局变量
.rodata:(只读数据段)里面放虚函数表(虚函数地址的数组,其中地址指向text)
.text:代码段
运行的时候会加载到内存,那么虚拟内存由哪几部分构成:
内核空间
栈区
文件映射区
堆区
数据区(静态存储区):.bss和.data映射到这里,.bss、.data
代码区:只读数据段(包含虚函数表)和代码段放到这里,.text、.rodata
进程的内存空间:
虚函数表指针在堆区指向虚函数表的地址,函数地址的指针又指向具体代码段的位置,那么虚函数与虚函数表指针的关系:
每个类最多只有一个虚函数表;(类中有virtual关键字修饰的时候才有且最多一个)
一个类的不同的对象(需要考虑浅拷贝和深拷贝),通常虚函数表指针不同,为了让每个对象都有自己的虚函数表指针,尽量使用深拷贝,显示的拷贝构造或重载赋值运算符(浅拷贝会共享一个虚函数表指针,深拷贝使另一个对象也有自己的虚函数表指针);
虚函数表指针就是指向虚函数表的指针
虚函数表的创建时机:
1、类对象构建的时候,把类的虚函数表地址赋值给vptr
2、如果没有构造函数的话,而类中又有虚函数表,那么编译器会生成默认的构造函数
3、继承的情况下(这是要理解多态的原理),虚函数表指针赋值过程:
a.调用基类构造函数,把A的虚函数表的地址赋值给vptr(虚拟函数表指针)
b.调用子类构造函数,把B的虚函数表的地址赋值给vptr(也就是赋值了两次)
class A{
public:
virtual void func();
};
class B:public class A{
};
多态:A* p=new B();关键点是,指针指向哪个类型的对象
虚函数表是类中有virtual关键字声明的函数,那么这个类在编译的时候就会为它生成虚函数表,存放着虚函数的函数地址,指向代码段中具体的代码位置