为什么要用虚析构和纯虚析构:
在使用多态时,如果子类中有属性开辟到堆区,那么我们在释放(或delete)父类的指针时无法调用子类的析构代码,释放子类在堆区的内存,会导致内存泄漏的问题。
eg:
#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)
{
this->m_name = new string(name);
cout << "这是Cat类的构造函数的调用" << endl;
}
virtual void speak()
{
cout <<*m_name<< "在叫" << endl;
}
~Cat()
{
if (this->m_name != NULL)
{
cout << "这是Cat类的析构函数调用" << endl;
delete m_name;
m_name = NULL;
}
}
private:
string* m_name;
};
int main()
{
Animal* a = new Cat("加菲猫");
a->speak();
delete a;
}
运行结果:
可以看到虽然Cat类中的m_name是存储在堆区的数据,但我们无法通过释放父类的指针调用子类的析构函数释放子类在堆区的内存。
解决方法:
将父类的析构函数变为虚析构或纯虚析构
虚析构和纯虚析构的共同点:
1.都可以解决释放父类指针时无法调用子类析构代码的问题。
2.二者都需要具体的函数实现。
虚析构和纯虚析构的不同点:
虚析构时可以正常实例化父类和子类的对象,但是纯虚析构会使父类变为抽象类,导致父类和子类都无法实例化对象。
虚析构的语法:
virtual ~类名(){}
纯虚析构的语法:
1.virtual ~类名()=0;
2.类名::~类名(){}
ps:虽然纯虚析构在类内直接=0;但依旧需要在类外实现,即类内声明,类外实现。
因为我们不知道父类是否有属性开辟到堆区,我们依旧需要父类有一个有具体实现的析构函数。
这点与纯虚函数不同!
上面代码使用虚析构后:
#include <iostream>
#include <string>
using namespace std;
class Animal
{
public:
Animal()
{
cout << "这是Animal类的构造函数调用" << endl;
}
virtual void speak() = 0;
virtual ~Animal()
{
cout << "这是Animal类的虚析构函数调用" << endl;
}
};
class Cat:public Animal
{
public:
Cat(string name)
{
this->m_name = new string(name);
cout << "这是Cat类的构造函数的调用" << endl;
}
virtual void speak()
{
cout <<*m_name<< "在叫" << endl;
}
virtual ~Cat()
{
if (this->m_name != NULL)
{
cout << "这是Cat类的析构函数调用" << endl;
delete m_name;
m_name = NULL;
}
}
private:
string* m_name;
};
int main()
{
Animal* a = new Cat("加菲猫");
a->speak();
delete a;
}
运行结果:
可以看到Cat类的析构函数被成功调用的同时Animal类的虚构函数的实现也是正常进行的。
上面代码使用纯虚析构后:
#include <iostream>
#include <string>
using namespace std;
class Animal
{
public:
Animal()
{
cout << "这是Animal类的构造函数调用" << endl;
}
virtual void speak() = 0;
virtual ~Animal() = 0;
};
Animal:: ~Animal()
{
cout << "这是Animal类的纯虚析构函数调用" << endl;
}
class Cat:public Animal
{
public:
Cat(string name)
{
this->m_name = new string(name);
cout << "这是Cat类的构造函数的调用" << endl;
}
virtual void speak()
{
cout <<*m_name<< "在叫" << endl;
}
virtual ~Cat()
{
if (this->m_name != NULL)
{
cout << "这是Cat类的析构函数调用" << endl;
delete m_name;
m_name = NULL;
}
}
private:
string* m_name;
};
int main()
{
Animal* a = new Cat("加菲猫");
a->speak();
delete a;
}
运行结果:
我认为主要要注意的地方是 区分纯虚函数和纯虚函数的不同:
纯虚函数不需要具体实现,只需要类中声明。
纯虚函数是需要具体实现的,只不过是在类外实现的。
并且二者都会使父类变为抽象类;