目录
1--前言
2--虚析构和纯虚析构
3--代码实例
3-1--父类对象无法调用子类析构函数
3-2--虚析构实现
3-3--纯虚析构实现
1--前言
在使用多态时,如果子类的属性开辟到堆区,那么父类指针在释放时将无法调用子类的析构代码,此时需要将父类中的析构函数修改为虚析构或者纯虚析构;
2--虚析构和纯虚析构
① 虚析构和纯虚析构的共性:
可以解决前言存在的问题,即解决父类指针无法调用子类析构函数释放堆区数据的问题;
虚析构和纯虚析构都需要具体的函数实现,即需实现函数体的内容;
② 虚析构和纯虚析构的区别:
纯虚析构的类为抽象类,不能实例化对象,而虚析构的类可以实例化对象;
// 虚析构语法:
virtual ~类名(){}
// 纯虚析构语法:
virtual ~类名() = 0;
3--代码实例
3-1--父类对象无法调用子类析构函数
# include <iostream>
# include <string>
class Animal{
public:
// 纯虚函数
Animal(){
std::cout << "Animal的构造函数调用" << std::endl;
}
~Animal(){
std::cout << "Animal的析构函数调用" << std::endl;
}
virtual void speak() = 0;
};
class Cat :public Animal{
public:
Cat(std::string name){
std::cout << "Cat的构造函数调用" << std::endl;
Name = new std::string(name);
}
virtual void speak(){
std::cout << *Name << " Cat is speaking" << std::endl;
}
~Cat(){
if(Name != NULL){
std::cout << "Cat的析构函数调用" << std::endl;
delete Name;
Name = NULL;
}
}
std::string *Name;
};
int main(){
Animal * animal = new Cat("Tom");
animal->speak();
// 父类指针析构时,不会调用子类的析构函数,如果子类含有堆区数据,会出现内存泄露
delete animal;
return 0;
}
程序执行结果表明:没有调用子类 Cat 的析构函数,堆区数据无法有效释放,导致内存泄露;
3-2--虚析构实现
# include <iostream>
# include <string>
class Animal{
public:
// 纯虚函数
Animal(){
std::cout << "Animal的构造函数调用" << std::endl;
}
// 利用虚析构可以解决父类指针释放子类对象的问题
virtual ~Animal(){
std::cout << "Animal的析构函数调用" << std::endl;
}
virtual void speak() = 0;
};
class Cat :public Animal{
public:
Cat(std::string name){
std::cout << "Cat的构造函数调用" << std::endl;
Name = new std::string(name);
}
virtual void speak(){
std::cout << *Name << " Cat is speaking" << std::endl;
}
~Cat(){
if(Name != NULL){
std::cout << "Cat的析构函数调用" << std::endl;
delete Name;
Name = NULL;
}
}
std::string *Name;
};
int main(){
Animal * animal = new Cat("Tom");
animal->speak();
// 父类指针析构时,不会调用子类的析构函数,如果子类含有堆区数据,会出现内存泄露
delete animal;
return 0;
}
3-3--纯虚析构实现
# include <iostream>
# include <string>
class Animal{
public:
// 纯虚函数
Animal(){
std::cout << "Animal的构造函数调用" << std::endl;
}
// 利用纯虚析构可以解决父类指针释放子类对象的问题
// 纯虚析构
virtual ~Animal() = 0;
virtual void speak() = 0;
};
// 纯虚析构也需要具体实现
Animal::~Animal(){
std::cout << "Animal的纯虚析构函数调用" << std::endl;
}
class Cat :public Animal{
public:
Cat(std::string name){
std::cout << "Cat的构造函数调用" << std::endl;
Name = new std::string(name);
}
virtual void speak(){
std::cout << *Name << " Cat is speaking" << std::endl;
}
~Cat(){
if(Name != NULL){
std::cout << "Cat的析构函数调用" << std::endl;
delete Name;
Name = NULL;
}
}
std::string *Name;
};
int main(){
Animal * animal = new Cat("Tom");
animal->speak();
// 父类指针析构时,不会调用子类的析构函数,如果子类含有堆区数据,会出现内存泄露
delete animal;
return 0;
}
通过实现父类的虚析构和纯虚析构,使得父类指针调用子类对象的析构函数,从而释放子类对象在堆区创建的数据,防止内存泄露;