背景
虚函数主要是用来实现多态的
虚函数在具体使用的时候才知道使用的是哪一个函数
如果虚函数指向的是子类 那么就从子类的虚函数表里面去找函数
如果指向的是父类 那么就从父类的虚函数表里面去找函数
1 虚函数表的创建时机是什么?
a.什么时候生成的?
编译器编译的时候生成的。在编译的时候 编译器发现virtual关键字的时候
b.存在的位置?
可执行程序(磁盘)
运行状态(内存)
下面会加载到存储区
(.bss 里面存储 未初始化 或者初始化为0的全局 或者静态变量)
(.data 里面存储 已经初始化 全局或者静态的变量)
(.rodata 只读的数据段会放我们的虚函数表)
虚拟内存的构建
内核空间(不可见的)
栈区
文件映射区
堆区
数据区(静态存储区 上面三部分会放在数据区)
代码区
虚函数表是虚函数地址的数组 虚函数指向代码区中的某个位置(具体的函数)
每个类只有一个虚函数表
一个类的不同的对象 基于此类的不同对象
需要考虑一下深拷贝和浅拷贝
如果是浅拷贝的对象 两个对象共享着一个虚函数表的指针 一个虚函数表指针丢失了 另外一个也会丢失
如果是深拷贝的对象 那么两个对象 是两个不同的空间 这部分空间可能存储同步相同地址的虚函数
有虚函数的时候 注意显示的定义拷贝构造函数和重载赋值运算符 使用深拷贝的时候 让两个对象各有自己的指针指向虚函数表
2 虚函数表指针的创建时机?
虚函数表指针就是指向虚函数表的指针
类对象构造的时候 吧类的虚函数表地址赋值给 vptr(虚函数表的地址-)
2.没有构造函数 编译器会生成默认的构造函数
3.继承的时候
如果B继承A
那么这个虚函数表的复制的过程是什么?
a.现调用基类的构造函数 把虚函数表的地址赋值给 b的vptr
b.然后调用子类的构造还能输 把B的虚函数表地址赋值给 vptr
如果b重载了a中的某个函数 就在虚函数表中重新创建一个地址来存
A * p = new B()
p 实际是B的实例化对象
p实际指向的是b的虚函数表的地址 所以p调用b重载a的函数的时候 是从b的虚函数表里面去找对应的函数