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

目录
相关文章
|
4月前
|
程序员 编译器 C++
【C++核心】C++内存分区模型分析
这篇文章详细解释了C++程序执行时内存的四个区域:代码区、全局区、栈区和堆区,以及如何在这些区域中分配和释放内存。
64 2
|
9天前
|
存储 算法 安全
基于哈希表的文件共享平台 C++ 算法实现与分析
在数字化时代,文件共享平台不可或缺。本文探讨哈希表在文件共享中的应用,包括原理、优势及C++实现。哈希表通过键值对快速访问文件元数据(如文件名、大小、位置等),查找时间复杂度为O(1),显著提升查找速度和用户体验。代码示例展示了文件上传和搜索功能,实际应用中需解决哈希冲突、动态扩容和线程安全等问题,以优化性能。
|
2月前
|
存储 编译器 数据安全/隐私保护
【C++】多态
多态是面向对象编程中的重要特性,允许通过基类引用调用派生类的具体方法,实现代码的灵活性和扩展性。其核心机制包括虚函数、动态绑定及继承。通过声明虚函数并让派生类重写这些函数,可以在运行时决定具体调用哪个版本的方法。此外,多态还涉及虚函数表(vtable)的使用,其中存储了虚函数的指针,确保调用正确的实现。为了防止资源泄露,基类的析构函数应声明为虚函数。多态的底层实现涉及对象内部的虚函数表指针,指向特定于类的虚函数表,支持动态方法解析。
34 1
|
2月前
|
Ubuntu Linux Shell
C++ 之 perf+火焰图分析与调试
【11月更文挑战第6天】在遇到一些内存异常的时候,经常这部分的代码是很难去进行分析的,最近了解到Perf这个神器,这里也展开介绍一下如何使用Perf以及如何去画火焰图。
113 5
|
3月前
|
编译器 C++
C++入门12——详解多态1
C++入门12——详解多态1
57 2
C++入门12——详解多态1
|
3月前
|
存储 算法 搜索推荐
对二叉堆的简单分析,c和c++的简单实现
这篇文章提供了对二叉堆数据结构的简单分析,并展示了如何在C和C++中实现最小堆,包括初始化、插入元素、删除最小元素和打印堆的函数,以及一个示例程序来演示这些操作。
46 19
|
3月前
|
Ubuntu Linux Shell
C++ 之 perf+火焰图分析与调试
【10月更文挑战第8天】在遇到一些内存异常的时候,经常这部分的代码是很难去进行分析的,最近了解到Perf这个神器,这里也展开介绍一下如何使用Perf以及如何去画火焰图。
|
3月前
|
C++
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
69 1
|
3月前
|
C++
C++入门13——详解多态2
C++入门13——详解多态2
94 1
|
4月前
|
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