C++多态之析构和纯虚析构分析与示例

简介: 虚析构和纯虚析构多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码解决方式:将父类中的析构函数改为虚析构或者纯虚析构虚析构和纯虚析构共性:• 可以解决父类指针释放子类对象• 都需要有具体的函数实现虚析构和纯虚析构区别:• 如果是纯虚析构,该类属于抽象类,无法实例化对象



虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码

解决方式:将父类中的析构函数改为虚析构或者纯虚析构

虚析构和纯虚析构共性:

  • 可以解决父类指针释放子类对象
  • 都需要有具体的函数实现

虚析构和纯虚析构区别:

  • 如果是纯虚析构,该类属于抽象类,无法实例化对象

虚析构语法:

virtual ~类名(){}

纯虚析构语法:

virtual ~类名() = 0;

类名::~类名(){}

示例:

classAnimal {

public:

   Animal()

   {

       cout<<"Animal 构造函数调用!"<<endl;

   }

   virtualvoidSpeak() =0;

   ~Animal()

   {

       cout<<"Animal虚析构函数调用!"<<endl;

   }

};

classCat : publicAnimal {

public:

   Cat(stringname)

   {

       cout<<"Cat构造函数调用!"<<endl;

       m_Name=newstring(name);

   }

   virtualvoidSpeak()

   {

       cout<<*m_Name<<  "小猫在说话!"<<endl;

   }

   ~Cat()

   {

       cout<<"Cat析构函数调用!"<<endl;

       if (this->m_Name!=NULL) {

           deletem_Name;//清除指针指向的堆区数据

           m_Name=NULL;//指针为空

       }

   }

public:

   string*m_Name;

};

voidtest01()

{

   Animal*animal=newCat("Tom");

   animal->Speak();

   deleteanimal;

}

intmain() {

   test01();

   system("pause");

   return0;

}

上述案例输出:

发现没有调用cat的析构函数,即堆区的内存没有被释放,内存泄漏。

问题产生原因:因为是用的父类的指针指向的子类对象Animal *animal = new Cat("Tom");所以当用delete父类指针时不会走子类的析构,导致子类如果有堆区的数据会出现内存的泄露情况。

通过父类指针去释放,会导致子类对象可能清理不干净,造成内存泄漏怎么解决?给基类增加一个虚析构函数虚析构函数就是用来解决通过父类指针释放子类对象时不干净的问题

classAnimal {

public:

   Animal()

   {

       cout<<"Animal 构造函数调用!"<<endl;

   }

   virtualvoidSpeak() =0;

   //析构函数加上virtual关键字,变成虚析构函数

   virtual~Animal()//虚析构函数就是用来解决通过父类指针释放子类对象时不干净的问题

   {

       cout<<"Animal虚析构函数调用!"<<endl;

   }

};

若使用纯虚析构时也可以解决

若是直接改成纯虚析构会报错

classAnimal {

public:

   Animal()

   {

       cout<<"Animal 构造函数调用!"<<endl;

   }

   virtualvoidSpeak() =0;

   //析构函数加上virtual关键字,变成虚析构函数

   virtual~Animal() =0

   

};

语法强制纯虚析构函数必须有函数实现,因为有时父类也有一些数据开辟在堆区,既要使用纯虚函数,又要释放父类在堆区中的数据,就需要使用类内纯虚函数声明,类外写实现的写法。

注意:区别于纯虚函数可以只写声明不写实现,纯虚析构需要声明也需要实现。有了纯虚析构后,这个类也属于抽象类,无法实例化对象。

classAnimal {

public:

   Animal()

   {

       cout<<"Animal 构造函数调用!"<<endl;

   }

   virtualvoidSpeak() =0;

   virtual~Animal() =0;

};

Animal::~Animal()

{

   cout<<"Animal 纯虚析构函数调用!"<<endl;

}

//和包含普通纯虚函数的类一样,包含了纯虚析构函数的类也是一个抽象类。不能够被实例化。

classCat : publicAnimal {

public:

   Cat(stringname)

   {

       cout<<"Cat构造函数调用!"<<endl;

       m_Name=newstring(name);

   }

   virtualvoidSpeak()

   {

       cout<<*m_Name<<  "小猫在说话!"<<endl;

   }

   ~Cat()

   {

       cout<<"Cat析构函数调用!"<<endl;

       if (this->m_Name!=NULL) {

           deletem_Name;//清除指针指向的堆区数据

           m_Name=NULL;//指针为空

       }

   }

public:

   string*m_Name;

};

voidtest01()

{

   Animal*animal=newCat("Tom");

   animal->Speak();

   //通过父类指针去释放,会导致子类对象可能清理不干净,造成内存泄漏

   //怎么解决?给基类增加一个虚析构函数

   //虚析构函数就是用来解决通过父类指针释放子类对象

   deleteanimal;

}

intmain() {

   test01();

   system("pause");

   return0;

}

由于本案例在一些子类中有些数据开辟到堆区了,所以必须要走子类中的析构代码,如果使用了多态就走不到了,所以需要加上虚析构或者纯虚析构。

总结:

1. 虚析构或纯虚析构就是用来解决通过父类指针释放子类对象

2. 如果子类中没有堆区数据,可以不写为虚析构或纯虚析构

3. 拥有纯虚析构函数的类也属于抽象类

目录
相关文章
|
6天前
|
JavaScript Java C语言
面向对象编程(C++篇3)——析构
面向对象编程(C++篇3)——析构
13 2
|
1月前
|
存储 编译器 C++
【C++】深度解剖多态(下)
【C++】深度解剖多态(下)
37 1
【C++】深度解剖多态(下)
|
1月前
|
存储 编译器 C++
|
26天前
|
机器学习/深度学习 算法 C++
C++多态崩溃问题之为什么在计算梯度下降时需要除以批次大小(batch size)
C++多态崩溃问题之为什么在计算梯度下降时需要除以批次大小(batch size)
|
1月前
|
Java 编译器 C++
【C++】深度解剖多态(上)
【C++】深度解剖多态(上)
35 2
|
26天前
|
机器学习/深度学习 PyTorch 算法框架/工具
C++多态崩溃问题之在PyTorch中,如何定义一个简单的线性回归模型
C++多态崩溃问题之在PyTorch中,如何定义一个简单的线性回归模型
|
1月前
|
编译器 程序员 C++
【C++高阶】掌握C++多态:探索代码的动态之美
【C++高阶】掌握C++多态:探索代码的动态之美
27 0
|
1月前
|
存储 编译器 C++
C++基础知识(七:多态)
多态是面向对象编程的四大基本原则之一,它让程序能够以统一的接口处理不同的对象类型,从而实现了接口与实现分离,提高了代码的灵活性和复用性。多态主要体现在两个层面:静态多态(编译时多态,如函数重载)和动态多态(运行时多态,主要通过虚函数实现)。
|
7天前
|
C++ 容器
C++中自定义结构体或类作为关联容器的键
C++中自定义结构体或类作为关联容器的键
13 0
|
7天前
|
存储 算法 搜索推荐
【C++】类的默认成员函数
【C++】类的默认成员函数