目录
多态实现
虚函数定义
先说原理
抛出问题
探究多态底层
冷门知识
多态实现
完成类多态体现,多态两个条件:
虚函数重写
父类指针或者引用去调用虚函数。
虚函数定义
虚函数重写/覆盖条件 : 函数 + 三同 (函数名、参数、返回值)
不符合重写,就是隐藏关系
特例1: 子类虚函数不加virtual,依旧构成重写(实际最好加上)
特例2:重写的协变。返回值可以不同,要求必须时交子关系的的指针或者引用(极易报错)
为什么呢?明明是father类引用呀。
先说原理
因为对象虚函数表的不同,基类的引用/指针其实指向的是子类的虚表,就好像基类函数给覆盖重写一样。
抛出问题
先写俩类。
class father
{
public:
virtual void func1()
{
printf("father printf\n");
}
virtual void no_vir()
{}
protected:
const char* _father_val = "fathar";
};
class son : public father
{
public:
virtual void func1()
{
printf("son printf\n");
}
protected:
const char* _son_val = "son";
};
这究竟是什么原因呢????
探究多态底层
进入调试模式查看father实例化对象parent里有啥。
_father_val我们知道是成员变量,这_vfptr是什么?
他是一个数组指针指向一个指针数组。这数组保存的是函数地址。这奇奇怪怪的什么情况看看parent的大小
这个二级指针属于这对象中。
看看child对象有什么。
自己_son_vals数据+ 父类继承虚表与父类_father_val.让我们对比一下,parent与child的虚表
首先vfptr虚表的地址不一样,说明了我们的child与parent对象的虚表是相互独立的,看看各自指向的数组,发现[1]保存的地址数据相同,而[0]的地址数据保存的不同。好关键的来啦
将parent对象与child对象都传入test函数。发现调用的函数不同。其实就是应为这的引用与指针调用函数其实是根据虚表的函数地址调用。
画图画图!
其实所谓的多态其实就是指针或者引用访问的时候派生类中继承基类虚表是,检测自己的函数是否可以重写基类虚函数,可以的话就会改变自己基类中虚表中该函数地址的值,改变为自己函数的地址。
冷门知识
我们的函数替换的时候其实保存的依旧是基类的函数接口。俗话讲:头还在换了个身体。因为在汇编中我们可以知道函数调用call前先需要,将实参压栈(如果写了缺省值就缺省压栈),然后再去调用call函数地址。
运行代码
重写替换了函数体内代码,但是函数头还是基类的头!!
如果有人这样考你,他一定是一个老六!!