多态分为两类:
- 静态多态: 函数重载 和 运算符重载 属于静态多态,复用函数名
- 动态多态: 派生类和虚函数实现运行时多态
静态多态和动态多态区别:
- 静态多态的函数地址早绑定 - 编译阶段确定函数地址
- 动态多态的函数地址晚绑定 - 运行阶段确定函数地址
多态的语法:
在类内成员函数前面加上virtual关键字,变成虚函数,那么编译器在编译的时候就不能确定函数调用了。
注意:写虚函数(非纯虚函数)时,一定要写函数内容,否则会出现 “无法解析外部符号“的错误!
多态的前提条件:
- 两个类或多个存在继承关系
- 子类重写父类中的虚函数
- 重写:函数返回值类型 函数名 参数列表 完全一致称为重写
多态的使用:父类的指针或引用指向子类对象
class person
{
public:
virtual void eat food() //在成员函数前面加virtual关键字,使之成为虚函数,virtual可省略
{
std::cout << "person eat food" << std::endl;
}
};
class xiaoming : public person //令person成为xiaoming的基类
{
public:
virtual void eat food()
{ // 派生类要重写基类的虚函数的定义
std::cout << "xiaoming eat food" << std::endl;
}
};
纯虚函数和抽象类:
在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容
因此可以将虚函数改为纯虚函数
纯虚函数语法:virtual 返回值类型 函数名 (参数列表)= 0 ;
当类中有了纯虚函数,这个类也称为抽象类
抽象类的特点:
- 无法实例化对象(无法创建对象)
- 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
虚析构和纯虚析构:
为什么要有虚析构和纯虚析构?:虚析构函数就是用来解决通过父类指针释放子类对象
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
换而言之,父类中的指针在释放时是无法调用子类的析构函数的,若子类中有属性开辟到堆区,以至于堆区的内存没有释放干净导致内存泄漏!
解决方法:将父类中的析构函数改为虚析构或纯虚析构。
与纯虚函数不同的是(纯虚函数不用写函数内容),纯虚析构和虚析构都必须要有函数的具体实现(即要有函数内容)!纯析构函数的定义要写在类外。
纯析构函数:
和包含普通纯虚函数的类一样,包含了纯虚析构函数的类也是一个抽象类。不能够被实例化。 (即纯虚析构函数与虚析构函数的区别)
总结:
- 虚析构或纯虚析构就是用来解决通过父类指针释放子类对象
- 如果子类中没有堆区数据,可以不写为虚析构或纯虚析构
- 拥有纯虚析构函数的类也属于抽象类