在使用C++多态时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码。例如 :
#include <iostream>
using namespace std;
#include <string>
class Animal {
public:
Animal()
{
cout << "Animal 构造函数调用!" << endl;
}
virtual void Speak() = 0;
//析构函数加上virtual关键字,变成虚析构函数
~Animal()
{
cout << "Animal虚析构函数调用!" << endl;
}
};
class Cat : public Animal {
public:
Cat(string name)
{
cout << "Cat构造函数调用!" << endl;
m_Name = new string(name);
}
virtual void Speak()
{
cout << *m_Name << "小猫在说话!" << endl;
}
~Cat()
{
cout << "Cat析构函数调用!" << endl;
if (this->m_Name != NULL) {
delete m_Name;
m_Name = NULL;
}
}
public:
string* m_Name;
};
void test01()
{
Animal* animal = new Cat("Tom");
animal->Speak();
delete animal;
}
int main() {
test01();
system("pause");
return 0;
}
运行结果:
我们在Cat类中通过堆区开辟内存,打算在析构函数中释放。然而从运行结果可以看出,Cat的析构函数没有被调用,因此现在代码会造成内存泄漏。
为了解决这个问题,可以将基类中的析构函数改为**虚析构** 或者** 纯虚析构**.
更改后的代码如下:
#include <iostream>
using namespace std;
#include <string>
class Animal {
public:
Animal()
{
cout << "Animal 构造函数调用!" << endl;
}
virtual void Speak() = 0;
//析构函数加上virtual关键字,变成虚析构函数
//virtual ~Animal()
//{
// cout << "Animal虚析构函数调用!" << endl;
//}
virtual ~Animal() = 0;
};
Animal::~Animal()
{
cout << "Animal 纯虚析构函数调用!" << endl;
}
//和包含普通纯虚函数的类一样,包含了纯虚析构函数的类也是一个抽象类。不能够被实例化。
class Cat : public Animal {
public:
Cat(string name)
{
cout << "Cat构造函数调用!" << endl;
m_Name = new string(name);
}
virtual void Speak()
{
cout << *m_Name << "小猫在说话!" << endl;
}
~Cat()
{
cout << "Cat析构函数调用!" << endl;
if (this->m_Name != NULL) {
delete m_Name;
m_Name = NULL;
}
}
public:
string* m_Name;
};
void test01()
{
Animal* animal = new Cat("Tom");
animal->Speak();
//通过父类指针去释放,会导致子类对象可能清理不干净,造成内存泄漏
//怎么解决?给基类增加一个虚析构函数
//虚析构函数就是用来解决通过父类指针释放子类对象
delete animal;
}
int main() {
test01();
system("pause");
return 0;
}
运行结果:
发现成功调用Cat类的析构函数,释放掉了堆区开辟的内存。
总结:
//多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
//解决方式:将父类中的析构函数改为**虚析构**或者**纯虚析构**
虚析构和纯虚析构共性:
* 可以解决父类指针释放子类对象
* 都需要有具体的函数实现
虚析构和纯虚析构区别:
* 如果是纯虚析构,该类属于抽象类,无法实例化对象