虚析构和纯虚析构
多态使用的时候,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码。因为这时如果删除父类指针指向的子类对象就不会触发动态绑定,只会调用父类的析构函数,而不会调用子类的析构函数,那么子类申请的空间就得不到释放了,会发生内存泄露
解决办法:将父类中的析构函数改为虚析构或纯虚析构
虚析构和纯虚析构的共性:
- 都可以解决父类指针无法释放子类对象的问题
- 都需要有具体函数的实现
虚析构和纯虚析构的区别:
有纯虚函数的类属于抽象类,无法实例化对象
虚析构语法:
virtual ~类名(){}
纯虚析构语法:
virtual ~类名() = 0;
类名::~类名(){}
先写出以下代码看看运行结果:
#include<iostream>
using namespace std;
#include<string.h>
class Animal
{
public:
Animal() {
cout << "Animal构造函数调用!" << endl;
}
~Animal() {
cout << "Animal析构函数调用!" << endl;
}
//纯虚函数
virtual void speak() = 0;
};
class Cat:public Animal
{
public:
Cat(string name) {//构造函数
cout << "Cat构造函数调用!" << endl;
mName=new string(name);//把字符串创建在堆区,并且让一个指针区维护堆区的数据
}
virtual void speak() {
cout << *mName<<"小猫在喵喵叫" << endl;
}
~Cat() {//析构函数
if (mName != NULL) {
cout << "Cat析构函数调用!" << endl;
delete mName;
mName = NULL;
}
}
string *mName;//指针
};
void test01() {
Animal* animal = new Cat("Tom");
animal->speak();
delete animal;
}
int main() {
test01();
system("pause");
return 0;
}
运行结果为:
可见,只调用了父类Animal的析构函数,并没有调用子类Cat的析构函数,所以需要对代码进行修改
利用虚析构解决:
~Animal()前面加上关键字virtual
//利用虚析构解决父类指针释放子类对象时释放不干净的问题
virtual ~Animal() {
cout << "Animal析构函数调用!" << endl;
}
运行结果为:
利用纯虚析构解决:
//虚析构和纯虚析构
class Animal
{
public:
Animal() {
cout << "Animal构造函数调用!" << endl;
}
//利用虚析构解决父类指针释放子类对象时释放不干净的问题
//virtual ~Animal() {
// cout << "Animal析构函数调用!" << endl;
//}
//纯虚析构
virtual ~Animal() = 0;
//纯虚函数
virtual void speak() = 0;
};
Animal:: ~Animal() {//纯虚析构的实现
cout << "Animal纯虚析构函数调用!" << endl;
}
使用纯虚析构时,不仅要声明,而且要在类外实现
运行结果: