代码:
enum class _ANIMALS_TYPE {
CAT,
DOG,
ANIMAL_COUNT
};
class Animal {
public:
Animal(_ANIMALS_TYPE type, int age,const char* name);
~Animal();
virtual void eat()const = 0;
private:
_ANIMALS_TYPE type; // 动物类型
int age; // 动物年龄
char* name = NULL;
};
class CAT : public Animal {
public:
CAT(_ANIMALS_TYPE type, int age, const char* name,const char* love);
~CAT();
void eat()const override;
private:
char* love = NULL;
};
int main(void) {
Animal* f = new CAT(_ANIMALS_TYPE::CAT, 5, "小猫", "小美");
delete f;
system("pause");
return 0;
}
Animal::Animal(_ANIMALS_TYPE type, int age, const char* name)
{
this->type = type;
this->age = age;
if (this->name) {
delete this->name;
}
int len = strlen(name) + 1;
this->name = new char[len];
strncpy(this->name, name, len);
}
Animal::~Animal()
{
cout << "调用Animal类的析构函数" << endl;
// 如果name非空释放内存
if (name) {
delete name;
}
}
CAT::CAT(_ANIMALS_TYPE type, int age,const char*name,const char*love):Animal(type,age,name)
{
if (this->love) {
delete this->love;
}
int len = strlen(love) + 1;
this->love = new char[len];
strncpy(this->love, love, len);
}
CAT::~CAT()
{
cout << "调用CAT类的析构函数" << endl;
if (love) {
delete love;
}
}
void CAT::eat() const
{
cout << "猫猫吃猫粮" << endl;
}
代码分析:
1. 我们之前讲过,如果类内部有开辟空间,那么就需要在析构函数中释放空间,上面就是这种情况,我们在父类和子类中都开辟了内存,并且在析构函数中释放了内存。
2. 我们使用多态的原则(使用父类的指针指向子类的对象),代码中我们使用new开辟了一片子类内存,然后使用父类指针指向了子类的这片内存。
3. 当开辟子类的内存时,会调用子类和父类的构造函数,那么就会给name和love属性开辟空间。
4. 我们给父类指针开辟空间之后,使用结束就要释放这个指针。 因为在类中有开辟空间,释放是就需要调用析构函数,将申请的内存释放掉。
5. 因为我们在申请内存的时候,父类的name和子类的love都开辟了内存,按理来说释放的时候都应该释放带哦,但是如图,释放时,只调用了父类的析构函数,也就是只释放了name。
这种情况,明显有问题,子类申请的内存不释放,就会造成内存泄露的风险。
原因:
其实和之前的原因类似,就是虽然我们使用父类的指针指向了子类,但是指针是父类的,在释放内存的时候自然只会调用父类的析构函数。
解决方法:
和之前的解决方式一样,就是在父类析构函数前加上virtual,实现虚析构函数。至于为什么,那就和之前的虚函数是类似的了。
加上之后:
这样在父类指针指向子类内存的时候,释放父类指针,不仅会调用父类的析构函数,也会调用子类的析构函数。
代码技巧:
为了防止上面的情况出现,我们建议如果一个类又子类就将析构函数加上virtual,如果有上面的情况,自然不会出问题,如果没有,加上也不影响。