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. 拥有纯虚析构函数的类也属于抽象类

目录
相关文章
|
11天前
|
Ubuntu Linux Shell
C++ 之 perf+火焰图分析与调试
简介 在遇到一些内存异常的时候,经常这部分的代码是很难去进行分析的,最近了解到Perf这个神器,这里也展开介绍一下如何使用Perf以及如何去画火焰图。 1. Perf 基础 1.1 Perf 简介 perf是Linux下的一款性能分析工具,能够进行函数级与指令级的热点查找。利用perf剖析程序性能时,需要指定当前测试的性能时间。性能事件是指在处理器或操作系统中发生的,可能影响到程序性能的硬件事件或软件事件 1.2 Perf的安装 ubuntu 18.04: sudo apt install linux-tools-common linux-tools-4.15.0-106-gen
15 2
|
1月前
|
JavaScript Java C语言
面向对象编程(C++篇3)——析构
面向对象编程(C++篇3)——析构
25 2
|
1月前
|
存储 编译器 C++
|
2月前
|
存储 编译器 C++
【C++】深度解剖多态(下)
【C++】深度解剖多态(下)
42 1
【C++】深度解剖多态(下)
|
1月前
|
存储 编译器 C++
C++多态实现的原理:深入探索与实战应用
【8月更文挑战第21天】在C++的浩瀚宇宙中,多态性(Polymorphism)无疑是一颗璀璨的星辰,它赋予了程序高度的灵活性和可扩展性。多态允许我们通过基类指针或引用来调用派生类的成员函数,而具体调用哪个函数则取决于指针或引用所指向的对象的实际类型。本文将深入探讨C++多态实现的原理,并结合工作学习中的实际案例,分享其技术干货。
38 0
|
2月前
|
机器学习/深度学习 算法 C++
C++多态崩溃问题之为什么在计算梯度下降时需要除以批次大小(batch size)
C++多态崩溃问题之为什么在计算梯度下降时需要除以批次大小(batch size)
|
2月前
|
Java 编译器 C++
【C++】深度解剖多态(上)
【C++】深度解剖多态(上)
41 2
|
2月前
|
机器学习/深度学习 PyTorch 算法框架/工具
C++多态崩溃问题之在PyTorch中,如何定义一个简单的线性回归模型
C++多态崩溃问题之在PyTorch中,如何定义一个简单的线性回归模型
|
2天前
|
编译器 C++
C++ 类构造函数初始化列表
构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式。
43 30
|
17天前
|
存储 编译器 C++
C ++初阶:类和对象(中)
C ++初阶:类和对象(中)