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

目录
相关文章
|
3月前
|
存储 人工智能 编译器
c++--多态
上一篇文章已经介绍了c++的继承,那么这篇文章将会介绍多态。看完多态的概念,你一定会感觉脑子雾蒙蒙的,那么我们先以举一个例子,来给这朦胧大致勾勒出一个画面,在此之前,先介绍一个名词虚函数,(要注意与虚拟继承区分)重定义: 重定义(隐藏)只要求函数名相同(但要符合重载的要求,其实两者实际上就是重载);重定义下:在这种情况下,如果通过父类指针或引用调用函数,会调用父类的函数而不是子类。重定义(或称为隐藏)发生的原因是因为函数名相同但参数列表不同,导致编译器无法确定调用哪一个版本的函数。
64 0
|
7月前
|
编译器 C++
c++中的多态
c++中的多态
|
6月前
|
存储 编译器 C++
【c++】多态(多态的概念及实现、虚函数重写、纯虚函数和抽象类、虚函数表、多态的实现过程)
本文介绍了面向对象编程中的多态特性,涵盖其概念、实现条件及原理。多态指“一个接口,多种实现”,通过基类指针或引用来调用不同派生类的重写虚函数,实现运行时多态。文中详细解释了虚函数、虚函数表(vtable)、纯虚函数与抽象类的概念,并通过代码示例展示了多态的具体应用。此外,还讨论了动态绑定和静态绑定的区别,帮助读者深入理解多态机制。最后总结了多态在编程中的重要性和应用场景。 文章结构清晰,从基础到深入,适合初学者和有一定基础的开发者学习。如果你觉得内容有帮助,请点赞支持。 ❤❤❤
763 0
|
8月前
|
存储 算法 安全
基于哈希表的文件共享平台 C++ 算法实现与分析
在数字化时代,文件共享平台不可或缺。本文探讨哈希表在文件共享中的应用,包括原理、优势及C++实现。哈希表通过键值对快速访问文件元数据(如文件名、大小、位置等),查找时间复杂度为O(1),显著提升查找速度和用户体验。代码示例展示了文件上传和搜索功能,实际应用中需解决哈希冲突、动态扩容和线程安全等问题,以优化性能。
|
10月前
|
存储 编译器 数据安全/隐私保护
【C++】多态
多态是面向对象编程中的重要特性,允许通过基类引用调用派生类的具体方法,实现代码的灵活性和扩展性。其核心机制包括虚函数、动态绑定及继承。通过声明虚函数并让派生类重写这些函数,可以在运行时决定具体调用哪个版本的方法。此外,多态还涉及虚函数表(vtable)的使用,其中存储了虚函数的指针,确保调用正确的实现。为了防止资源泄露,基类的析构函数应声明为虚函数。多态的底层实现涉及对象内部的虚函数表指针,指向特定于类的虚函数表,支持动态方法解析。
108 1
|
10月前
|
Ubuntu Linux Shell
C++ 之 perf+火焰图分析与调试
【11月更文挑战第6天】在遇到一些内存异常的时候,经常这部分的代码是很难去进行分析的,最近了解到Perf这个神器,这里也展开介绍一下如何使用Perf以及如何去画火焰图。
381 5
|
11月前
|
编译器 C++
C++入门12——详解多态1
C++入门12——详解多态1
120 2
C++入门12——详解多态1
|
11月前
|
Ubuntu Linux Shell
C++ 之 perf+火焰图分析与调试
【10月更文挑战第8天】在遇到一些内存异常的时候,经常这部分的代码是很难去进行分析的,最近了解到Perf这个神器,这里也展开介绍一下如何使用Perf以及如何去画火焰图。
188 1
|
11月前
|
C++
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
118 1
|
11月前
|
C++
C++入门13——详解多态2
C++入门13——详解多态2
159 1