多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方式:将父类中的析构函数改为虚析构或者纯虚析构
先看一个案例,子类在堆区开辟了空间:
#include<iostream>
#include<string>
using namespace std;
class animal
{
public:
animal()
{
cout << "animal的构造函数" << endl;
}
virtual void speak() = 0;//纯虚函数
~animal()
{
cout << "animal的析构函数" << endl;
}
};
class cat:public animal
{
public:
cat(string name) //构造函数
{
cout << "cat的构造函数" << endl;
m_name = new string(name);//在堆区开辟内存
}
virtual void speak()//子类中的virtual 加不加都行
{
cout <<*m_name<< "小猫在说话" << endl;
}
~cat()
{
if (m_name != NULL)
{
delete m_name;
m_name = NULL;
}
cout << "cat的析构函数" << endl;
}
public:
string* m_name;
};
void main()
{
animal* an = new cat("Tom");
an->speak();
delete an;//父类指针析构的时候,不会调用子类的析构,造成内存泄露
}
按照顺序,创建对象时,父类的构造先调用,之后调用子类的构造;
删除对象时,应该先调用子类的析构,在调用父类的析构;但是现在没有调用子类的析构,而且子类有堆区开辟的空间,这就造成了内存没有释放干净,造成了内存泄露。
解决办法:使用虚析构
#include<iostream>
#include<string>
using namespace std;
class animal
{
public:
animal()
{
cout << "animal的构造函数" << endl;
}
virtual void speak() = 0;//纯虚函数
/*~animal()
{
cout << "animal的析构函数" << endl;
}*/
virtual~animal()//虚析构:解决了父类对象释放子类内存,这时候就回去调用子类的析构
{
<< "animal的析构函数" << endl;
}
};
class cat:public animal
{
public:
cat(string name) //构造函数
{
cout << "cat的构造函数" << endl;
m_name = new string(name);//在堆区开辟内存
}
virtual void speak()//子类中的virtual 加不加都行
{
cout <<*m_name<< "小猫在说话" << endl;
}
~cat()
{
if (m_name != NULL)
{
delete m_name;
m_name = NULL;
}
cout << "cat的析构函数" << endl;
}
public:
string* m_name;
};
void main()
{
animal* an = new cat("Tom");
an->speak();
delete an;//父类指针析构的时候,不会调用子类的析构,造成内存泄露
}
在父类的析构函数前加:virtual 就可以了;这时候子类的析构就调用了。
这时也可以使用纯虚析构:
class animal
{
public:
animal()
{
cout << "animal的构造函数" << endl;
}
virtual void speak() = 0;//纯虚函数
/*virtual~animal()//虚析构:解决了父类对象释放子类内存,这时候就回去调用子类的析构
{
<< "animal的析构函数" << endl;
} */
virtual~animal() = 0; //属于抽象类,无法实例化对象
//纯虚析构,但是纯虚析构不能与纯虚函数一样为空;是必须要写实现的,因为父类中也可能有一些资源需要释放。
};
//可以在类外写纯虚析构的具体实现,那么类内就是声明
animal::~animal()
{
cout << "animal的纯析构函数" << endl;
}
总结:
虚析构和纯虚析构共性:
- 可以解决父类指针释放子类对象
- 都需要有具体的函数实现
虚析构和纯虚析构区别:
- 如果是纯虚析构,该类属于抽象类,无法实例化对象
虚析构语法:
virtual ~类名(){}
纯虚析构语法:
virtual ~类名() = 0;
类名::~类名(){}
注意:
如果子类中没有堆区数据,可以不写虚析构或纯虚析构