一、问题引入
使用多态时,如果有一些子类的成员开辟在堆区,那么在父类执行完毕释放后,没有办法去释放子类的内存,这样会导致内存的泄漏。如下代码段。
如果没有堆区的数据,可以不写虚析构或纯虚析构。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
#include <iostream>
#include <string>
using namespace std;
class Animal{
public:
Animal(){
cout<<"Animal-构造"<<endl;
}
~Animal(){
cout<<"Animal-析构"<<endl;
}
virtual void Run()=0; //纯虚函数无需实现,只需声明
};
class Cat:public Animal{
public:
string *s_name;
Cat(string name){
s_name = new string(name);//在堆区创建内存
cout<<"Cat-构造"<<endl;
}
void Run()
{
cout<<*s_name<<"->"<<"Cat-Run"<<endl;
}
~Cat(){
cout<<"Cat-析构"<<endl;
if(s_name!=NULL){
delete s_name;
s_name=NULL;
}
}
};
int main()
{
Animal *a;
a = new Cat("Tom");
a->Run();
delete a; //父类指针析构的时候,不会调用子类析构函数
return 0;
}
|
运行结果:
结果可以看到都有父类和子类的构造,虽然在主函数中delete 父类,但最终只有父类的析构函数,此时子类在堆区创建的s_name并没有得到释放,导致内存泄漏。
以上的问题我们引入虚析构和纯虚析构来解决——父类指针释放子类对象时不干净的问题
二、利用虚析构解决
虚析构只需要在析构函数前加关键字 virrtual 即可,再观察结果,可以看到父类和子类的都执行了析构函数,而子类中在堆区创建的数据也被释放干净,这是最终的结果!
1
2
3
|
virtual ~Animal(){
cout<<"Animal析构"<<endl;
}
|
三、利用纯虚析构解决
纯虚析构格式如下,和纯虚函数有点类似,但需要有具体的声明和具体的实现。纯虚析构需要在类外实现.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class Animal{
public:
Animal(){
cout<<"Animal-构造"<<endl;
}
//虚析构
/*virtual ~Animal(){
cout<<"Animal析构"<<endl;
}*/
//纯虚析构
virtual ~Animal()=0;
virtual void Run()=0; //纯虚函数无需实现,只需声明
};
//需要有声明,也需要有实现
Animal::~Animal(){
cout<<"纯虚析构"<<endl;
}
|
结果如下,和虚析构有相同的作用
四、总结
虚析构和纯虚析构
相同点: 都可以解决父类指针释放子类对象,都需要有具体的实现
不同点: 纯虚析构属于抽象类,无法实例化对象
|