| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> C++知识库 -> C++ 析构函数的坑 -> 正文阅读 |
|
[C++知识库]C++ 析构函数的坑 |
C++ 的析构函数,通常是用来在生命周期结束时释放对象的。最近看到了关于析构函数的一些坑,本文会有介绍,并不是最全的,但也算是一些记录。 1、什么时候编译器会生成析构函数?每一个类都会存在析构函数,对于类类型(class type),如? 对于一般的类类型而言,通常其生成的析构函数会是空的(empty body),所以在内联之后,直接就等同于消失了。 什么时候会看到有实现体的析构函数? 通常是继承链当中,存在某个类自定义了析构函数,那么编译器为了满足继承链上的析构,会为继承链上该类的每个子类都生成有实现体的析构函数。
在上面这个案例当中,编译器会为 Child 类生成有实现体的析构函数,后面会有讲到原因,作为对比,Grand 类的是 empty body,直接 inline 消失了。 还有一个前提,那就是代码中需要存在对 Child 类的使用,不然编译器直接就不用生成它的析构函数的实现体了。 这是什么原因?因为在语法树当中,编译器首先会给它生成 implicit、inline、default 属性的析构函数声明。
在代码中,如果存在对 Child 类的析构函数的使用需要,这时候编译器才开始为其生成相关的实现体(LLVM 实现)。 2、继承链的析构函数2.1、栈对象对于栈上的对象而言,析构函数会顺着继承链逆向进行调用:
为什么可以调到 Father 类里面的析构函数?来看下编译器生成的简单的汇编指令(为讲解方便,删去了其他指令):
实际上,自定义的析构函数并非完全是自定义的行为,编译器会给子类的析构函数添加调用指令,从而能够调用继承链上自定义的析构函数。 2.2、堆对象
注意,这里的析构函数的调用,是由? 这里只调用了 Father 类的析构函数,是因为编译器生成的析构函数调用,是根据其类型来进行判断的,而这里的变量 f 是 Father 类型。
那么怎么样才能让编译器识别到需要从 Child 的析构函数调起呢? 答案是使用?
这样编译器就认识到,这个 Father 类的析构函数是个虚函数,会走虚表调用函数的方式,从实际对象类型的析构函数调起。如果不使用? 2.3、访问权限(access)对于析构函数的调用,需要是 public 的访问权限,否则会导致编译错误。
实际上,对于访问权限的保证,在同一个编译单元内,是由编译器来进行保证的。而在不同的编译单元内,是由链接符号(private 的函数不会对外暴露符号)来进行保证的。 在语法树层面,会专门有一个 Decl 来表明其下方所有的 Decl 的访问权限。默认没找到则按其类类型的默认选项来确定(class 为 private,struct 为 public)。
3、纯虚析构函数对于基类的纯虚析构函数,其必须要有定义存在,否则会存在链接报错,因为基类的析构函数,会在子类被析构的时候调用到。
4、析构函数里的调用猜猜下面这段代码会输出什么结果?
其实这段代码的结果,结合本文 2.2 的分析,编译器会为? 而?
但为什么普通虚函数和析构函数的调用产生的行为会不一样?一个是直接调用,而另一个是虚表调用。 Standard mandates that the runtime type of the object is that of the class being constructed/destructed at this time, even if the original object that is being constructed/destructed is of a derived type. C++ 标准里面规定了构造和析构是采用运行时类型,即使当前实际对象是其子类,所以构造函数和析构函数走的都是直接调用的方式。 5、析构函数与异常机制很多文章都描述了,如果在析构函数里,没有进行捕获住内部异常的操作是非常危险的行为。 C++ 11 之后,析构函数默认是? 为了能够传递异常,需要标注? 那么危险在哪里呢?
危险就在于析构函数可能是在栈展开(Stack unwinding)的时候被调用,也就是正在进行异常处理的过程中,如果又出现一个异常抛出,这时候程序被中止,因为不允许同时处理两个及以上数量的异常。 安全起见,析构函数也需要包一层异常的捕获。
5.1、智能指针在使用智能指针时,析构函数的异常一定不能被抛到外面去,即使外面能够捕获且外面无异常。
参考Destructors:https://en.cppreference.com/w/cpp/language/destructor How Does Virtual Destructor Works:http://www.vishalchovatiya.com/part-3-all-about-virtual-keyword-in-c-how-virtual-destructor-works/ throwing exceptions out of a destructor:https://stackoverflow.com/quest |
|
C++知识库 最新文章 |
【C++】友元、嵌套类、异常、RTTI、类型转换 |
通讯录的思路与实现(C语言) |
C++PrimerPlus 第七章 函数-C++的编程模块( |
Problem C: 算法9-9~9-12:平衡二叉树的基本 |
MSVC C++ UTF-8编程 |
C++进阶 多态原理 |
简单string类c++实现 |
我的年度总结 |
【C语言】以深厚地基筑伟岸高楼-基础篇(六 |
c语言常见错误合集 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 | -2024/12/26 16:45:52- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |
数据统计 |