一、什么是多态
二、多态的实现条件
继承体系+虚函数重写+基类调用
虚函数是在前面virtual的成员函数。
![](https://i-blog.csdnimg.cn/blog_migrate/4245e52cda3d0a9ba15a64f1a6f765f1.png)
这里无论是引用p,还是指针ptr,都是看其指向对象的类型来调用函数,即形成了多态。
重写的2个例外:
1、协变
![](https://i-blog.csdnimg.cn/blog_migrate/d7f15e70acf179b95303523de80848f1.png)
这里父子类可以不是虚函数所在的类,可以是任意的父子类。
2、析构函数
由于向上转换的存在,C++允许一个父类指针指向子类对象。
此时,按照以前的理解,delete p,根据p的类型去调用基类的析构函数,就会导致派生类没有被清理,进而导致内存泄漏等问题。
这里我们期望实现多态调用析构函数:即根据p指向的类型是派生类,然后去调用派生类的析构。
但是不同类的析构函数的函数名不同,该怎么办呢?
这里编译器将所有的析构函数在底层都替换为destructor,这样满足三同,然后再分别重写各自的析构函数,就实现多态调用析构函数了。
总结:为了实现多态调用析构函数,必须将其变为虚函数。
重载隐藏重写的区别
C++11关键字
1、override
在基类虚函数中加override,会自动检查子类虚函数是否重写。
2、final
基类虚函数加final则不能被重写。
如果想要实现类不能被继承,可以将其构造函数私有,就无法在其派生类中自动调用了。
当要调用A类对象的构造时,可以使用静态函数返回一个构造好的A类对象。
或者直接在类后面加final。
三、虚函数表
C++创建对象模型时,如果类中有虚函数,会多一个_vfptr的函数指针,指向虚函数表,表中是类所有虚函数的地址。
一个类的所有对象共用一张虚表。
派生类的虚表生成:
存储位置
![](https://i-blog.csdnimg.cn/blog_migrate/25b8892c6d0cae2e64b94b9acec121a8.png)
虚表存在常量区/代码段。
用基类指针/引用实现多态的原因
打印虚表
ps中,有3个函数。
st中,重写了BuyTicket所以地址改变,Func1和Func2没有重写,仍为拷贝过来的原地址
自己的Func3跟在自己的虚表后面。
动态/静态多态
编译时只检查语法,指针不知道调用的是哪个函数,要在运行的时候去虚函数表里面找然后调用
重载在编译时根据函数名修饰规则就确定了,直接根据函数地址调用即可。
虚函数调用原理:
四、多继承下的虚表
多继承体系下,Derive继承了两个类,分别将它们的虚表也继承下来
func2没有重写,因此与原来的地址相同。
func1都重写了,为什么两个地址不一样?
这是由于C++对象模型导致的。ptr2指向的是Derive对象中的base2对象,指针或引用要指向这个Derive对象,而不是它其中的base2对象,因此要先调整到前4个字节,然后传this指针指向虚表指针,然后再调用。
这里不同编译器的处理结果也不同,也可能是相同的地址。VS2022下是不同的。