多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方式:将父类中的析构函数改为虚析构或者纯虚析构
先看一个案例,子类在堆区开辟了空间:
#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()
{
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;
virtual~animal()
{
<< "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()
{
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() = 0;
};
animal::~animal()
{
cout << "animal的纯析构函数" << endl;
}
总结:
虚析构和纯虚析构共性:
- 可以解决父类指针释放子类对象
- 都需要有具体的函数实现
虚析构和纯虚析构区别:
虚析构语法:
virtual ~类名(){}
纯虚析构语法:
virtual ~类名() = 0;
类名::~类名(){}
注意:
如果子类中没有堆区数据,可以不写虚析构或纯虚析构
|