虚析构和纯虚析构

1、问题分析

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码。但是我们也有解决办法:将父类中的析构函数改为虚析构或者纯虚析构。当然再了解如何解决这个具体问题之前,还需要知道以下四点知识。

  • 虚析构和纯虚析构共性:第一点是可以解决父类指针释放子类对象;第二点是都需要有具体的函数实现。

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

  • 虚析构语法:

    virtual~类名(){}
  • 纯虚析构语法:

    virtual~类名() = 0 ;
    
    类名::~类名(){} 

2、代码展现

那代码如何实现呢?是下面这段代码吗?

#include<iostream>
using namespace std;
class Anmial
{
       //纯虚函数
       virtual void speak() = 0;
};
class Cat :public Anmial
{
       
       //子类重写父类的speak()函数,不要省略virtual,否则无法打印;
       virtual void speak()   
       {
              cout << "小猫在说话" << endl;
       }
};
void test01()
{
       //父类指针指向子类对象
       Anmial* anmial = new Cat;
       //anmial调用子类函数
       anmial->speak();
       //由于创建在堆区,所以需要程序员手动释放
       delete  anmial;
}
int main()
{
       test01();
       system("pause");
       return 0;
}

当然不是这段代码,由于我的大意,忘记在Anmial 和Cat,这两个类中加上public,结果就出现报错。

 那么这个报错解决了,再想一下,如果我们想给小猫起个名字,那代码应该怎么写呢?可以自己先尝试改一下,之后不妨看一下,下面这段代码!

#include<iostream>
using namespace std;
#include<string>
class Anmial
{
public:
       Anmial()
       {
              cout << "父类Anmial的构造函数调用" << endl;
       }
       ~Anmial()
       {
              cout << "父类Anmial的析构函数调用" << endl;
       }
       //纯虚函数
       virtual void speak() = 0;
};
class Cat :public Anmial
{
public:
       
       Cat(string name)
       {
              cout << "子类Cat的构造函数调用" << endl;
              m_Name = new string(name);
       }
       //子类重写父类的speak()函数,不要省略virtual,否则无法打印;
       virtual void speak()   
       {
              cout << * m_Name<<"小猫在说话" << endl;
       }
       string* m_Name;  //在堆区创建一个名字,用指针来维护它
       ~Cat()
       {
              if (m_Name != NULL)
              {
                      cout << "Cat析构函数调用" << endl;
                      delete m_Name;
                      m_Name = NULL;
              }
       }
};
void test01()
{
       //父类指针指向子类对象
       Anmial* anmial = new Cat("Tom");
       //anmial调用speak函数
       anmial->speak();
       //由于创建在堆区,所以需要程序员手动释放
       delete  anmial;
}
int main()
{
       test01();
       system("pause");
       return 0;
}

既然,我们已经写到这一步,为什么在VS运行结果却没有“子类Cat析构函数调用”这句话呢?

其实是这样的,父类指针再析构时,不会调用子类中析构函数,导致子类如果有堆区属性,会出现内存泄漏。既然说到这,我们也有解决办法,就是在父类析构函数前面virtual。如:virtual ~Anmail()。

3、完整的代码展示

#include<iostream>
using namespace std;
#include<string>

class Animal
{
public:
       Animal()
       {
              cout << "父类Anmial的构造函数调用" << endl;
       }
       //虚析构   可以解决,父类指针释放子类对象时不干净的问题
       /*virtual ~Animal()   //
       {
              cout << "父类Anmial的析构函数调用" << endl;
       }*/  
       //虚析构和纯虚析构只需要一个就可以了
       //纯虚析构,需要声明,也需要实现
       //有了纯虚析构之后,这个类也属于抽象类,无法实例化对象
       virtual ~Animal() = 0;
       纯虚函数
       virtual void speak() = 0;
};
Animal :: ~Animal()
{
       cout << "animal纯虚析构函数调用" << endl;
}

class Cat :public Animal
{

public:
       
       Cat(string name)
       {
              cout << "子类Cat的构造函数调用" << endl;
              m_Name = new string(name);
       }

      // 子类重写父类的speak()函数,不要省略virtual,否则无法打印;
       virtual void speak()   
       {
              cout << * m_Name<<"小猫在说话" << endl;
       }
       string* m_Name;  //在堆区创建一个名字,用指针来维护它
       ~Cat()
       {
              if (m_Name != NULL)
              {
                      cout << "子类中Cat析构函数调用" << endl;
                      delete m_Name;
                      m_Name = NULL;
              }
       }
};
void test01()
{
       //父类指针指向子类对象
       Animal* animal = new Cat("Tom");
       anmial调用speak函数
       animal->speak();
       //由于创建在堆区,所以需要程序员手动释放
       //父类指针在析构时候,不会调用子类中析构函数,导致子类如果有堆区属性,出现内存泄漏
       delete  animal;
}
int main()
{
       test01();
       system("pause");
       return 0;
}

分析如下: 

 

4、回顾

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值