多态可分为:
-
静态多态:重载(地址早绑定)
-
动态多态:派生类和虚函数(地址晚绑定)
多态的前提:
-
要有继承关系
-
子类要重写父类中的虚函数
-
虚函数、虚函数指针、虚函数表(多态底层原理)
我们要想实现多态,就要将某个父类函数设置为虚函数。就是在函数名前加上virtual关键字。这时,父类的结构就会发生变化,父类中会出现一个虚函数指针。这个虚函数指针指向父类中的虚函数入口地址,此时若发生继承关系,子类就会把父类中的这个虚函数指针(vfptr)继承下去,这是如果我们在子类中不重写父类的虚函数,这个vfptr指向的依然是父类的虚函数入口地址;但是,如果我们发生了重写操作,vfptr指向的虚函数表中存放的就是子类重写后的函数入口地址了。这种现象叫做覆盖。这时我们用父类指针来接受一个子类对象,子类对象中的属性通过拷贝构造将自身的vfptr等其他属性赋给这个父类指针,并且,当我们通过这个父类指针来调用函数时,调用的就是子类中的函数了。
并且,我的理解是:调用函数一定时通过函数地址来的,也就是说 父类指针通过这个vfptr来调用函数的。(这些只是本人拙见,只是为了帮助自己更好理解,望网友根据自身情况采纳)
纯虚函数和抽象类
一般发生继承时,父类中的各种函数没有实际作用和意义的,这是我们就可以将其设置为纯虚函数。语法: virtual 返回值类型 函数名() = 0;一个类中有纯虚函数,我们就称这个类为抽象类。
注意:
不能通过抽象类来实例化对象
如若子类不重写父类的纯虚函数,则子类也是一个抽象类
纯虚析构和虚析构
在我们使用多态时,用一个父类指针指向堆区子类对象,但是这样我们并不能通过这个父类指针来走到子类中的析构函数,也就是说,如若子类中有堆区开辟的数据就会得不到释放,这是我们可以将父类中的析构函数设置为虚析构函数,这样我们就可以通过父类指针走到子类中的析构函数,那么,理所应当,子类中的堆区数据就可以在其析构函数中得以释放,就可以解决内存泄漏的问题。
注意:
如若父类中的析构函数时纯虚析构函数,则父类就是一个抽象类。