虚析构函数、纯虚析构函数

C++笔记之虚析构函数的作用 如果基类的析构函数不是虚函数,那么只会调用基类的析构函数,而不会调用派生类的析构函数。虚析构函数是一种特殊类型的析构函数,在基类中声明为虚函数,并且在派生类中可以被重写。它用于正确释放派生类对象的资源,即使通过基类指针删除派生类对象时也能够调用正确的析构函数。它与类的构造函数相对应,当对象的生命周期结束时(例如对象被销毁或超出其作用域),析构函数会被自动调用。总结来说,当使用继承和多态性时,通常应该将基类的析构函数声明为虚函数,并在派生类中重写它,以确保正确释放派生类对象的资源。函数中,通过基类指针。 阅读详情
一. 虚析构函数

我们知道,为了能够正确的调用对象的析构函数,一般要求具有层次结构的顶级类定义其析构函数为虚函数。因为在delete一个抽象类指针时候,必须要通过虚函数找到真正的析构函数。

如:
class Base
{
public:
   Base()
{}
   
virtual ~Base(){}
}
;

class Derived: public Base
{
public:
   Derived()
{};
   
~Derived(){};
}


void foo()
{
   Base 
*pb;
   pb 
= new Derived;
   delete pb;
}
 
这是正确的用法,会发生动态绑定,它会先调用Derived的析构函数,然后是Base的析构函数

如果析构函数不加virtual,delete pb只会执行Base的析构函数,而不是真正的Derived析构函数。
因为不是virtual函数,所以调用的函数依赖于指向静态类型,即Base

二. 纯虚析构函数
现在的问题是,我们想把Base做出抽象类,不能直接构造对象,需要在其中定义一个纯虚函数。如果其中没有其他合适的函数,可以把析构函数定义为纯虚的,即将前面的CObject定义改成:
class Base
{
public:
   Base()
{}
   
virtual ~Base()= 0
}
;
可是,这段代码不能通过编译,通常是link错误,不能找到~Base()的引用(gcc的错误报告)。这是因为,析构函数、构造函数和其他内部函数不一样,在调用时,编译器需要产生一个调用链。也就是,Derived的析构函数里面隐含调用了Base的析构函数。而刚才的代码中,缺少~Base()的函数体,当然会出现错误。

这里面有一个误区,有人认为,virtual f()=0这种纯虚函数语法就是没有定义体的语义。
其实,这是不对的。这种语法只是表明这个函数是一个纯虚函数,因此这个类变成了抽象类,不能产生对象。我们完全可以为纯虚函数指定函数体 。通常的纯虚函数不需要函数体,是因为我们一般不会调用抽象类的这个函数,只会调用派生类的对应函数。这样,我们就有了一个纯虚析构函数的函数体,上面的代码需要改成:
class Base
{
public:
   Base()
   
{
   }

   
virtual ~Base() = 0; //pure virtual
}
;

Base::
~Base()//function body
{
}
 
从语法角度来说,不可以将上面的析构函数直接写入类声明中(内联函数的写法)。这或许是一个不正交化的地方。但是这样做的确显得有点累赘

这个问题看起来有些学术化,因为一般我们完全可以在Base中找到一个更加适合的函数,通过将其定义为没有实现体的纯虚函数,而将整个类定义为抽象类。但这种技术也有一些应用,如这个例子:
class Base  //abstract class
{
public:
   
virtual ~Base(){};//virtual, not pure
   virtual void Hiberarchy() const = 0;//pure virtual
}
;

void Base::Hiberarchy() const //pure virtual also can have function body
{
   std::cout 
<<"Base::Hiberarchy";
}


class Derived : public Base
{
public:
   Derived()
{}
   
virtual void Hiberarchy() const
   
{
       CB::Hiberarchy();
       std::cout 
<<"Derived::Hiberarchy";
   }

   
virtual void foo(){}
}
;


int main(){
   Base
* pb=new Derived();
   pb
->Hiberarchy();
   pb
->Base::Hiberarchy();
   
return 0;
}
 
在这个例子中,我们试图打印出类的继承关系。在根基类中定义了虚函数Hiberarchy,然后在每个派生类中都重载此函数。我们再一次看到,由于想把Base做成个抽象类,而这个类中没有其他合适的方法成员可以定义为纯虚的,我们还是只好将Hiberarchy定义为纯虚的。(当然,完全可以定义~Base函数,这就和上面的讨论一样了。^_^)

另外,可以看到,在main中有两种调用方法,第一种是普通的方式,进行动态链接,执行虚函数,得到结果"Derived::Hiberarchy";第二种是指定类的方式,就不再执行虚函数的动态链接过程了,结果是"Base::Hiberarchy"。

通过上面的分析可以看出,定义纯虚函数的真正目的是为了定义抽象类,而并不是函数本身。与之对比,在java中,定义抽象类的语法是 abstract class,也就是在类的一级作指定(当然虚函数还是也要加上abstract关键字)。是不是这种方式更好一些呢?在Stroustrup的《C++语言的设计与演化》中我找到这样一段话:

“我选择的是将个别的函数描述为纯虚的方式,没有采用将完整的类声明定义为抽象的形式,这是因为纯虚函数的概念更加灵活一些。我很看重能够分阶段定义类的能力;也就是说,我发现预先定义一些纯虚函数,并把另外一些留给进一步的派生类去定义也是很有用的”。

我还没有完全理解后一句话,我想从另外一个角度来阐述这个概念。那就是,在一个多层复杂类结构中,中间层次的类应该具体化一些抽象函数,但很可能并不是所有的。中间类没必要知道是否具体化了所有的虚函数,以及其祖先已经具体化了哪些函数,只要关注自己的职责就可以了。也就是说,中间类没必要知道自己是否是一个真正的抽象类,设计者也就不用考虑是否需要在这个中间类的类级别上加上类似abstract的说明了。

当然,一个语言的设计有多种因素,好坏都是各个方面的。这只是一个解释而已。

最后,总结一下关于虚函数的一些常见问题:

1) 虚函数是动态绑定的,也就是说,使用虚函数的指针和引用能够正确找到实际类的对应函数,而不是执行定义类的函数。这是虚函数的基本功能,就不再解释了。 

2) 构造函数不能是虚函数。而且,在构造函数中调用虚函数,实际执行的是父类的对应函数,因为自己还没有构造好, 多态是被disable的。 

3) 析构函数可以是虚函数,而且,在一个复杂类结构中,这往往是必须的。
 
4) 将一个函数定义为纯虚函数,实际上是将这个类定义为抽象类,不能实例化对象。 

5) 纯虚函数通常没有定义体,但也完全可以拥有。

6)  析构函数可以是纯虚的,但纯虚析构函数必须有定义体,因为析构函数的调用是在子类中隐含的。 

7) 非纯的虚函数必须有定义体,不然是一个错误。 

8) 派生类的override虚函数定义必须和父类完全一致。除了一个特例,如果父类中返回值是一个指针或引用,子类override时可以返回这个指针(或引用)的派生。例如,在上面的例子中,在Base中定义了 virtual Base* clone(); 在Derived中可以定义为 virtual Derived* clone()。可以看到,这种放松对于Clone模式是非常有用的。 
其他,有待补充。
详解C++中虚析构函数的作用及其原理分析 主要介绍了C++中虚析构函数的作用及其原理分析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 立即下载

相关推荐

【Attention Is All You Need】Transformer模型精读

Attention Is All You Need》这篇论文奠定了Transformer模型的基础,为深度学习的发展提供了新的方向。Transformer通过自注意力机制和并行化处理显著提升了计算效率和模型性能。然而,Transformer的广泛应用也带来了新的挑战,如计算资源的消耗和对长序列的处理能力等。随着技术的不断发展,研究人员已经提出了诸如Efficient Transformer、Reformer等变种,旨在提升Transformer的效率和适应性。

Lewiz_124的博客 1499

虚析构和纯虚析构函数

5.1 虚析构 5.1.1 virtual ~类名() {} 5.1.2 解决问题: 通过父类指针指向子类对象释放时候不干净导致的问题 5.2 纯虚析构函数 5.2.1 写法 virtual ~类名() = 0 5.2.2 类内声明 类外实现 5.2.3 如果出现了纯虚析构函数,这个类也算抽象类,不可以实例化对象 析构函数 #define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<string> using names

Scarlett的博客 1000

c++界面开发入门

c++界面开发 DialogBox MessageBox

jiaodou2的博客 6516

多态 进阶(虚析构,纯虚函数,纯虚析构)

虚函数 纯虚函数 虚析构 纯虚析构(重要) 重写 重载 重定义(了解)

兰天禄的博客 3534

利用HFSS仿真优化圆极化微带天线的耦合馈电设计

本文深入探讨了利用HFSS仿真软件优化圆极化微带天线性能的关键方法。通过对比传统直接馈电与耦合馈电方式,详细阐述了耦合馈电在提升阻抗匹配、改善轴比带宽及增强设计鲁棒性方面的显著优势。文章结合FR4板材的实战案例,逐步演示了在HFSS中建立模型、进行参数扫描与圆极化性能优化的完整流程,为设计高性能、高纯度的圆极化天线提供了实用指南。

weixin_29233333的博客 547

四足机器人如何用对抗运动先验(AMP)实现复杂地形行走?Unitree Go1实战解析

本文深入解析了四足机器人Unitree Go1如何通过对抗运动先验(AMP)算法实现复杂地形行走。AMP结合模仿学习与对抗训练,使机器人仅凭本体感知就能适应未知地形。文章详细介绍了AMP的核心架构、仿真训练工程实践以及在Unitree Go1上的部署优化方案,展示了其在极端地形下的卓越性能表现。

weixin_29050513的博客 327

纯虚析构函数

纯虚析构函数和普通纯虚函数的区别在于,纯虚析构函数需要提供函数的实现,而一般纯虚函数不能有实现,这样的原因在于,纯虚析构函数最终需要被调用,以析构基类对象,虽然是抽象类没有实体。而如果不提供该析构函数的实现,将使得在析构过程中,析构无法完成而导致析构异常的问题 Calss A {    public:    A(){}    virtual ~A()=0; } A::~A(){}

7708

Qt6.10 | Qt Bluetooth 蓝牙

本文介绍了QtBluetooth模块的功能和使用方法,包括蓝牙API概述、构建配置(CMake/qmake)、权限管理、平台支持(Linux/macOS特殊处理)、日志设置及许可信息。重点说明Linux平台的后端选择机制,以及macOS上的依赖限制。最后提及蓝牙聊天示例,展示RFCOMM协议通信的实现。全文提供了蓝牙开发的技术要点和注意事项。

Qt历险记,专注Qt技术,三年医疗设备研发经验,1年机器视觉研发经验,半年柔性传感研发经验。 399

C++虚析构函数和纯虚析构函数

1、为什么要使用虚析构函数 我们知道析构函数是在对象生命周期结束时自动被调用,用来做一些清理工作(如释放句柄,释放堆内存等),防止出现内存泄漏。 那怎么还有虚析构函数呢? 使用虚析构函数的类一般是要作为基类,被其他类继承。通过把基类的析构函数声明为虚函数,就可以通过父类指针来释放子类对象,从而完成子类的一些清理工作,防止出现内存泄漏。 案例1:基类析构函数为非虚函数 //test.h class Parent { public: Parent(); ~Parent

技术成就梦想,梦想成就未来。If you try hard enough, it can be. 2634

19、利用PostGIS进行空间分析:更高效的矢量分析方法

本文介绍了如何利用PostGIS进行高效灵活的矢量空间分析,重点探讨了在选址安静住宅场景中使用缓冲区和距离测量两种方法的实现与优化。通过QGIS与PostGIS结合,演示了数据上传、CTE应用、ST_Intersects与ST_Distance函数对比,并深入分析了横向子查询在提升查询性能和代码可读性方面的优势。文章还比较了不同方法的适用场景,并以房地产开发选址为例展示了完整分析流程,最后总结了PostGIS在空间分析中的强大能力与未来应用前景。

d3e4f的博客 92

VSCode 中搭建 Python 开发环境

**验证安装**:打开终端(CMD/PowerShell/终端),输入 `python --version`(Windows)或 `python3 --version`(macOS/Linux),显示版本号即成功。- **下载地址**:[Python 官网](https://www.python.org/downloads/)(选择对应系统的最新稳定版,推荐 3.8+)- **调试**:通过 `launch.json` 配置,设置断点后按 `F5` 启动,使用 `F10/F11` 控制流程。

寒寒豆豆软件公司的博客 2534

简述虚析构函数与纯虚析构函数

基础知识: C++中,一个类在建成时,需要有构造函数初始化。即当一个类的对象在生成时,会先调用构造函数初始化,再执行接下来的其他函数。而在一个类的对象指针被释放的时候,会调用析构函数。即使析构函数不被声明,也会隐式调用析构函数。 那么,当一个派生类继承了一个基类,这时候调用析构函数,会发生什么呢? #include <iostream> using namespace std;...

wufeifan_learner的博客 2030

【如何学习CAN总线测试】——Python+Robot Framework框架实现UDS诊断自动化

由于项目需要进行UDS诊断测试,所以对这方面进行了研究学习,网上很少能查询到相关资料,故记录一下UDS自动化测试开发过程,由于保密原则,案例都是Demo,希望能帮到感兴趣的朋友。

测试领域技术分享 7465

C++虚析构函数与纯虚析构函数

C++虚析构函数与纯虚析构函数 虚析构函数 析构函数的工作方式是:最底层的派生类(most derived class)的析构函数最先被调用,然后调用每一个基类的析构函数。 因为在C++中,当一个派生类对象通过使用一个基类指针删除,而这个基类有一个非虚的析构函数,则结果是未定义的。运行时比较有代表性的后果是对象的派生部分不会被销毁。然而,基类部分很可能已被销毁,这就导致了一个

chenglinhust的专栏 1359

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

纯虚析构函数的作用

<br />      在某些类里声明纯虚析构函数很方便。纯虚函数将产生抽象类——不能实例化的类(即不能创建此类型的对象)。有些时候,你想使一个类成为抽象类,但刚好又没有任何纯虚函数。怎么办?因为抽象类是准备被用做基类的,基类必须要有一个虚析构函数,纯虚函数会产生抽象类,所以方法很简单:在想要成为抽象类的类里声明一个纯虚析构函数。<br />  这里是一个例子:<br />  class awov {<br />  public:<br />  virtual ~awov() = 0; // 声明一个纯虚析

World of iOs 4165

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

C++基础---虚析构函数与纯虚析构函数

1. 虚析构函数与纯虚析构函数 1.1 虚析构函数与纯虚析构函数的定义 虚析构函数:为了解决基类的指针指向派生类对象,并用基类的指针删除派生类对象; 虚析构函数:当且仅当类里包含至少一个虚函数的时候才去声明虚析构函数,无故的声明虚析构函数和永远不去声明一样是错误的; class <类名> { virtual ~<类名>(); … }; 即:virtual ~A(); 纯虚析构函数:抽象类是准备被用做基类的,基类

Catherine的笔记 1795

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

C++纯虚析构函数

图说C++对象模型:对象内存布局详解:https://blog.youkuaiyun.com/suchto/article/details/54947869?fps=1&amp;locationNum=9原文:https://blog.youkuaiyun.com/yby4769250/article/details/7294733纯虚析构函数和普通纯虚函数的区别在于,纯虚析构函数需要提供函数的实现,而一般纯虚函数不能有...

折腾怪的博客 9425

析构函数、虚析构函数、纯虚析构函数

我们知道对象在结束其生命周期之前,都会调用析构函数以完成必要的清理工作;派生类调用的析构函数顺序是“先子类,后基类”; 这篇文章用于总结当析构函数是普通析构函数、虚析构函数、纯虚析构函数时,我们使用delete运算符删除一个指针对象时,析构函数会有什么情况发生; 普通析构函数 CBase是基类,CDerive是其子类,类源码代码如下: class CBase { public: CBase(){} //基类析构函数 ~CBase(){ cout << "CBase Des

sinat_36002055的博客 1559

纯虚析构函数必须有定义(可以是空的)

纯虚析构函数必须有定义(可以是空的),一般就给一个空的 #include class A { public: explicit A(int x) : m_nX(x) {} virtual ~A() = 0;// 纯虚析构函数必须有定义并且会执行 virtual void f1() = 0 /*{ std::cout << "A::f1()" << std::endl; }*/;// 纯

qq_34581491的博客 1596

纯虚析构函数和非纯虚析构函数

纯虚机构函数和非纯虚析构函数之间唯一的不同之处在于纯虚析构函数使得基类是抽象类,不能创建基类的对象。 纯虚析构函数的特点:必须为纯虚析构函数提供一个函数体。

vinnyjia的博客 350

6.4虚析构函数和纯虚析构函数

虚析构函数和纯虚析构函数

qq_52563729的博客 1152

C++ 虚析构函数和纯虚析构函数

#include <iostream> using namespace std; /************************************************************************/ /* 虚析构函数和纯析构函数 1.当父类指针或引用指向子类对象时,若子类中数据成员有在堆区申请空间,父类析构函数需要 声明为虚析构函数或纯析构函数,析构时子类的析构函数会被调用,堆区空间会被释放,否则子类对象会出现内存泄漏问题 2.纯虚析构函数需要进行声明和类外.

CodeHouse的博客 628

纯虚基类与纯虚析构函数

本文探讨了纯虚基类中析构函数的特殊性问题。文章指出,纯虚基类的析构函数必须提供函数体,即使是纯虚析构函数也需要在类外显式定义。纯虚析构函数的设计主要是为了解决语法问题,使没有合适纯虚函数的类也能成为纯虚基类。与普通虚析构函数相比,纯虚析构函数在功能上没有特殊性。编译器不能自动合成纯虚析构函数定义,因为这违反纯虚函数的语义规则,并可能导致重复定义问题。文章最后解释了为什么虚析构函数可以自动合成而纯虚析构函数不能。

jifeidanda123的博客 357
上一篇: android:shape的使用 (android用xml文件生成图像控件)
下一篇: C++ 虚基类、虚继承
panfengsoftware
博客等级 码龄20年 38粉丝 34原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值