1、多态基本概念2、纯虚函数和抽象类3、虚析构和纯虚析构 1、多态基本概念
举个简单栗子,构造一个动物类,设定动物都会叫;再构造一个猫类和一个狗类,猫类会喵喵,狗类会汪汪,就是两个子类中也有父类中实现的方法(有重载那味了)。
多态分为两类:
静态多态:函数重载和运算符重载属于静态多态,复用函数名动态多态:派生类和虚函数(virtual)实现运行时多态
两者区别:
静态多态的函数地址早绑定,编译阶段确定函数地址动态多态的函数地址晚绑定,运行阶段确定函数地址
动态多态满足的条件:
有继承关系子类重写父类中的虚函数动态多态使用:父类的指针或者引用指向子类对象(例如子类中编写函数void Speak(Animal &animal))
使用多态的好处:
组织结构清晰可读性强分别前期和后期扩展以及维护性高 2、纯虚函数和抽象类在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容,因此可以将虚函数改为纯虚函数
通俗点讲,写纯虚函数的目的就是想提醒子类中必需要实现这样一个函数,你不实现就给报错,就根本不能算是它的一个子类
纯虚函数语法:
virtual 返回值类型 函数名 (参数列表) = 0;
virtual void func() = 0;
当类中有了纯虚函数,这个类也称为 抽象类(抽象,懂吧,跟Java一个道理)
抽象类特点:
无法实例化对象子类必须重写抽象类中的纯虚函数,否则也属于抽象类 3、虚析构和纯虚析构
多态使用时,如果子类中有属性开辟到堆区(比如new了一个指针),那么父类指针在释放时会无法调用到子类的析构代码
解决方式:将父类中的析构函数改为 虚析构 或者 纯虚析构
就这么理解:
当子类中用函数new了一个东西出来:Son *son = new m_A,而在main函数里有个父类指针new了一个子类出来:base *base = new Son,当程序运行完后,父类的析构函数运行了,但子类的析构函数没有运行,导致son指针没有释放。这时候就需要将父类的析构函数设置为虚析构。
3.1 虚析构写法:
virtual ~类名(){}
virtual ~base(){
}
3.2 纯虚析构写法:
virtual ~类名() = 0;
类名::~类名(){……}
virtual ~base() = 0;
base :: ~base(){
}
代码实现
#include
输出结果为:
仔细点看,可以发现Cat析构函数并没有被调用,可是我代码中明明就有一行 "Cat析构函数调用完成"啊?
原因在于,父类指针在析构时候,没有调用子类中的析构函数,导致子类如果有堆区属性,就会出现内存泄漏
解决方法很简单,将父类析构函数改为虚析构:
virtual ~Animal(){cout << "Animal析构函数调用" << endl;}
修改后的输出结果:
注意:
纯虚析构属于抽象类,无法实例化对象,子类中必需要重写析构函数虚析构或纯虚析构都是用来解决通过父类指针释放子类对象的问题