条款9:利用destructors避免泄露资源

对指针说拜拜。承认吧,你从未真正喜欢过它,对不?

好,你不需要对所有指针说拜拜,但是你真的得对那些用来操控局部性资源(local resources)的指针说莎唷娜拉了。

举个例子,你正在为“小动物收养保护中心”(一个专门为小狗小猫寻找收养家庭的组织)编写一个软件。收养中心每天都会产生一个文件,其中有它所安排的当天收养个案。你的工作就是写一个程序,读这些文件,然后为每一个收养个案做适当处理。


合理的想法是定义一个抽象基类(abstract base class)ALA("Adorable Litle Animal"),再从中派生出针对小狗和小猫的具体类(concrete classes)。其中有个虚函数processAdoption,负责“因动物种类而异”的必要处理动作。

class ALA{
public:
virtual void processAdoption()= 0;
...
};class Puppy: public ALA
{
public:
virtual  void processAdoption();
...
};class Kitten: public ALA
{
public:
virtual void processAdoption ();
...
};

你需要一个函数,读取文件内容,并视文件内容产生一个Puppyobject或一个Kitten object。这个任务非常适合用virtual constructor完成,那是条款25中描述的一种函数。

对本目的而言,以下声明便是我们所需要的:

//从s读取动物信息,然后返回一个指针,指向一个
// 新分配的对象,有着适当的类型(Puppy或Kitten)
ALA * readALA(istream& s);

你的程序核心大约是一个类似这样的函数:

void processAdoptions(istream& dataSource)
{while (datasource)//如果还有数据,{ALA* pa = readALA(dataSource);//取出下一只动物,pa->processAdoption();//处理收养事宜,delete pa;//删除readALA返回的对象。}
}

这个函数走遍dataSource,处理它所获得的每一条信息。

唯一需要特别谨慎的是,它必须在每次迭代的最后,记得将pa删除。这是必要的,因为每当readALA被调用,便产生一个新的 heap object。如果没有调用 delete,这个循环很快便会出现资源泄漏的问题。

现在请考虑:如果pa->processAdoption抛出一个exception,会发生什么事情。processAdoptions 无法捕捉它,所以这个 exception 会传播到 processAdoptions的调用端。processAdoptions 函数内“位于pa->processAdoption 之后的所有语句”都会被跳过,不再执行,这也意味pa不会被删除。结果呢,只要pa->processAdoption 抛出一个 exception,processAdoptions 便发生一次资源泄漏。

要避免这一点,很简单:

void processAdoptions(istream& dataSource)
{while (dataSource){ALA* pa = readALA(dataSource),try {pa->processAdoption();}catch (...)//捕捉所有的exceptions。{delete pa;//当exception 被抛出,避免资源泄漏。throw;//将exception 传播给调用端。}delete pa;//如果没有exception被抛出,也要避免资源泄漏。}
}

但你的程序因而被try 语句块和catch 语句块搞得乱七八糟。

更重要的是,你被迫重复撰写其实可被正常路线和异常路线共享的清理代码(cleanup code)本例指的是delete动作。

这对程序的维护造成困扰,撰写时很烦人,感觉也不理想。不论我们是以正常方式或异常方式(抛出一个exception)离开processAdoptions函数,我们都需要删除pa,那么何不集中于一处做这件事情呢?

其实不必大费周章,只要我们能够将“一定得执行的清理代码”移到processAdoptions函数的某个局部对象的destructor 内即可。因为局部对象总是会在函数结束时被析构,不论函数如何结束(唯一例外是你调用longjmp而结束。longjmp 的这个缺点正是C++ 支持exceptions的最初的主要原因)。于是,我们真正感兴趣的是,如何把delete 动作从 processAdoptions 函数移到函数内某个局部对象的destructor内。

解决办法就是,以一个“类似指针的对象”取代指针pa,如此一来,当这个类似指针的对象被(自动)销毁,我们可以令其destructor 调用delete。

“行为类似指针(但动作更多)”的对象我们称为smart pointers。如条款28所言,你可以做出非常灵巧的“指针类似物”。本例倒是不需要什么特别高档的产品,我们只要求它在被销毁(由于即将离开其scope)之前删除它所指的对象,就可以啦。

技术上这并不困难,我们甚至不需要自己动手。C++标准程序库(见条款E49)提供了一个名为auto_ptr的class template,其行为正是我们所需要的。每个auto_ptr的constructor 都要求获得一个指向 heap object 的指针;其destructoy会将该heapobject 删除。

如果只显示这些基本功能,auto_ptr 看起来像这样:

template<class T>
class auto_ptr
{
public:auto_ptr(T* p = 0) :ptr(p) {}// 存储对象。~auto ptr(){delete ptr;// 删除对象。}private:T* ptr;//原始指针(指向对象).
};

auto_ptr 标准版远比上述复杂得多。上述这个剥掉一层皮的东西并不适合实际运用2(至少还需加上copy constructor,assignment operator 及条款28所讨论的指针仿真函数operator*和operator->),但是其背后的观念应该很清楚了:以auto_ptr 对象取代原始指针,就不需再担心heap objects没有被删除一一即使是在exceptions被抛出的情况下。

注意,由于auto ptr destructor 采用“单一对象”形式的delete,所以auto ptr 不适合取代(或说包装)数组对象的指针。如果你希望有一个类似 autoptr的template可用于数组身上,你得自己动手写一个。不过如果真是这样,或许更好的选择是以vector 取代数组。
以auto_ptr对象取代原始指针之后,processAdoptions 看起来像这样:

void processAdoptions(istream& dataSource)
{while (dataSource){auto_ptr<ALA> pa(readALA(dataSource)pa->processAdoption();}
}

这一版和原先版本的差异只有两处。

  • 第一,pa被声明为一个 auto_ptr<ALA>对象,不再是原始的 ALA*指针;
  • 第二,循环最后不再有delete 语句。就这样啦,其他每样东西都没变,除了析构动作外,auto_ptr 对象的行为和正常指针完全一样。很简单,不是吗?

隐藏在auto ptr背后的观念一—以一个对象存放“必须自动释放的资源”,并依赖该对象的destructor 释放一—亦可对“以指针为本”以外的资源施行。

考虑图形界面(GUT)应用软件中的某个函数,它必须产生一个窗口以显示某些信息:

//此函数可能会在抛出一个 exception 之后发生资源泄漏问题。
void displayInfo(const Informations info)
{WINDOW_HANDLE w(createwindow());displayinfo in window corresponding to w,destroyWindow(w);
}

许多窗口系统都有 C语言接口,运行诸如 createWindow 和 destroyWindow这类函数,取得或释放窗口(被视为一种资源)。如果在信息显示于的过程中发生exception,w所持有的那个窗口将会遗失,其他动态分配的任何资源也会遗失。

解决之道和先前一样,设计一个class,令其constructor 和destructor 分别取得资源和释放资源:

//这个class 用来取得及释放一个 window handle。
class WindowHandle {
public:WindowHandle(WINDOW HANDLE handle) :w(handle) {}~WindowHandle() {destroyWindow(w);}operator WINDOW_HANDLE(){return w;//详述于下。}
private:WINDOW_HANDLE w;//以下函数被声明为private,用以阻止产生多个 WINDOW HANDLE。//条款28讨论了一个更弹性的做法。WindowHandle(const WindowHandle&);WindowHandle& operator=(const WindowHandle&);
};

这看起来就像auto_ptr template 一样,只不过其赋值动作(assignment)和复制动作(copying)被明确禁止了(见条款E27)。

此外,它有一个隐式类型转换操作符,可用来将一个 windowHandle 转换为一个 WINDOW HANDLE。

这项能力对于WindowHandleobject 的实用性甚有必要,意味你可以像在任何地方正常使用原始的WINDOW_HANDLE一样使用一个 windowHandle(不过条款5也告诉你为什么应该特别小心隐式类型转换操作符)。
 


有了这个 WindowHandle class,我们可以重写 displayInfo如下:

// 此函数可以在exception 发生时避免出现资源泄漏问题。
void displayInfo(const Information& info)
{WindowHandle w(createwindow());display info in window corresponding to w;
}

现在即使 displayInfo函数内抛出 exception,createWindow 所产生的窗口还是会被销毁。

只要坚持这个规则,把资源封装在对象内,通常便可以在exceptions 出现时避免泄漏资源。

但如果exception 是在你正取得资源的过程中抛出的,例如在一个“正在抓取资源”的class constructor 内,会发生什么事呢?

如果exception 是在此类资源的自动析构过程中抛出的,又会发生什么事呢?此情况下constructors和destructors 是否需要特殊设计?

是的,它们需要,你可以在条款 10和条款 11中学到这些技术。
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/17574.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

godot4.2 + GDextension c++在 vs code 中断点调试配置

游戏开发中如果做不到自己编写的代码做断点调试&#xff0c;无不是瞎子摸象&#xff0c;特别是C这么底层的语言。这2天开始在VS studio中折腾&#xff0c;一直折腾不出结果&#xff0c;几次想要放弃GODOT。最终今天在VS code中搞定了这断点调试C代码。 在上一篇文章我已经做好了…

全网爆火Remini 粘土滤镜风格,我用ComfyUI一键生成了(保姆级教程)!

一、火爆全网的Remini&#xff01; Remini真的火爆了&#xff01;最近大家的朋友应该都被粘土滤镜刷屏了。 小红书上粘土滤镜、粘土特效的帖子动不动就是几百万浏览量&#xff0c;几千赞。 在有些电商平台上还有人接单&#xff0c;帮忙定制remini粘土风格的照片&#xff01; …

vue+three.js实现3d系统的搭建

1.首先node.js是12.22版本的&#xff0c;安装three.js可以参考这篇文章 直接用Threejs入门-安装教程_安装three.js-CSDN博客 直接在终端安装three.js即可 npm install --save three 在相同目录下安装vite构建工具 npm install --save-dev vite 在项目里面看package.json中…

【安装笔记-20240528-Linux-在 Vultr 云服务器上安装 OpenWRT】

安装笔记-系列文章目录 安装笔记-20240528-Linux-在 Vultr 云服务器上安装测试 OpenWRT 文章目录 安装笔记-系列文章目录安装笔记-20240528-Linux-在 Vultr 云服务器上安装测试 OpenWRT 前言一、软件介绍名称&#xff1a;OpenWRT主页官方介绍 二、安装步骤测试版本&#xff1a…

案例研究|MeterSphere助力万物云构建高效自动化测试平台

万物云空间科技服务股份有限公司&#xff08;以下简称为“万物云”&#xff09;&#xff0c;前身为万科物业发展股份有限公司&#xff0c;是国内领先的物管龙头上市公司。作为一家科技引领的全域空间服务商&#xff0c;万物云致力于打造产业级共享服务平台&#xff0c;基于空间…

酒店提前线上订房小程序源码系统 PHP+MySQL组合开发 源码开源可二开 带完整的安装代码包以及搭建教程

系统概述 随着移动互联网的普及&#xff0c;越来越多的人习惯通过手机进行酒店预订。传统的线下订房方式逐渐无法满足用户的需求&#xff0c;酒店提前线上订房小程序的出现成为必然趋势。该源码系统的开发旨在为酒店提供一个便捷、高效的线上订房平台&#xff0c;提升用户体验…

基于微信小程序+ JAVA后端实现的【医院挂号预约系统】 设计与实现 (内附设计LW + PPT+ 源码+ 演示视频 下载)

项目名称 项目名称&#xff1a; 《基于微信小程序的医院挂号预约系统设计与实现》 项目技术栈 该项目采用了以下核心技术栈&#xff1a; 后端框架/库&#xff1a; Java, SSM框架数据库&#xff1a; MySQL前端技术&#xff1a; 微信小程序, uni-app 项目展示 全文概括 本…

MySQL触发器实战:自动执行的秘密

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 &#x1f38f;&#xff1a;你只管努力&#xff0c;剩下的交给时间 &#x1f3e0; &#xff1a;小破站 MySQL触发器实战&#xff1a;自动执行的秘密 前言触发器的定义和作用触发器的定义和作用触发器的…

SAP 根据报错消息号快速定位问题

通常用户在业务的操作过程中&#xff0c;经常会遇到报错信息&#xff0c;有些报错是系统控制抛出的信息&#xff0c;但是有些报错的信息是根据不同地点业务场景对填写的数据进行判断校验&#xff0c;然后给出的报错信息&#xff0c;正常情况报错信息一般是有文本&#xff0c;或…

【C语言】文件操作讲解

C语言文件操作讲解 文件文件名文件类型数据在内存中的存储 文件缓冲区文件指针文件的打开与关闭fopenfclosefopen与fclose的使用文件的打开方式 文件的顺序读写fputcfgetcfputsfgetsfprintffscanffwritefread输入流与输出流对比scanf\fscanf\sscanf与printf\fprintf\sprintfssc…

汇编原理(二)寄存器——内存访问

一个字 两个字节 双字 字节为8位 字为16位&#xff08;看两格&#xff09; 双子dword32位&#xff08;看四格&#xff09; 内存中字的存储&#xff1a; 0地址单元中存放的字节型数据是多少&#xff1f; 0地址字单元中存放的字型数据是多少&#xff1f; 2地址字单元中存放…

Secure Operation

文章目录 Secure Summation OperationSecure Set Union Operation Secure Summation Operation 让我们通过一个具体的例子来说明这个算法。 假设有三个数据拥有者 S1, S2 和 S3&#xff0c;他们分别持有以下值&#xff1a; S1 持有 value1 10S2 持有 value2 20S3 持有 val…

基坑气膜:建筑工地环保新利器—轻空间

随着城市化进程的加快&#xff0c;建筑行业的飞速发展带来了严重的环境问题&#xff0c;如噪音和粉尘污染&#xff0c;给人们的生活带来诸多不便。为了解决这些问题&#xff0c;建筑行业一直在探索更为环保和高效的施工方式。近年来&#xff0c;基坑气膜技术逐渐崭露头角&#…

Audition 2024 for Mac/Win:音频录制与编辑的卓越之选

随着数字媒体的不断发展&#xff0c;音频内容创作已经成为各行各业中不可或缺的一部分。无论是音乐制作、广播节目、播客录制还是影视配音&#xff0c;都需要高品质的音频录制和编辑工具来实现专业水准的作品。在这个充满竞争的时代&#xff0c;要想在音频创作领域脱颖而出&…

解线性方程组——最速下降法及图形化表示 | 北太天元 or matlab

一、思路转变 A为对称正定矩阵&#xff0c; A x b Ax b Axb 求解向量 x x x这个问题可以转化为一个求 f ( x ) f(x) f(x)极小值点的问题&#xff0c;为什么可以这样&#xff1a; f ( x ) 1 2 x T A x − x T b c f(x) \frac{1}{2}x^TAx - x^Tb c f(x)21​xTAx−xTbc 可…

ZooKeeper安装

安装Zookeeper 1、下载Zookeeper安装包 打开链接选择一个版本进行下载 https://zookeeper.apache.org/releases.html2、上传Zookeeper安装包到集群 输入命令 scp apache-zookeeper-3.8.4-bin.tar.gz hadoop192.168.88.100:/tmp也可以使用xftp等上传&#xff0c;物理机用u盘…

《精通Stable Diffusion AI绘画:基础技巧、实战案例与海量资源一站式学习》

随着人工智能技术的迅猛发展&#xff0c;AI绘画已经成为了一个炙手可热的话题。特别是在设计、艺术和创意领域&#xff0c;AI绘画工具的出现无疑为创作者们带来了更多的可能性和便利。《Stable Diffusion AI绘画从提示词到模型出图》这本书&#xff0c;就是一本深入解析Stable …

恢复视频3个攻略:从不同情况下的恢复方法到实践!

随着科技的进步&#xff0c;我们的生活被各种各样的数字内容所包围&#xff0c;其中&#xff0c;视频因其独特的记录性质&#xff0c;承载着许多重要的资料。但不管是自媒体人还是普通人日常生活随手一拍&#xff0c;都会遇到误删视频的情况。为了帮助您找回手机视频&#xff0…

从零学爬虫:使用比如说说解析网页结构

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言 二、网页结构概述 示例&#xff1a;查看网页结构 三、使用比如说说解析网页 1.…

windows10更改文件默认打开软件

&#x1f4da;博客主页&#xff1a;knighthood2001 ✨公众号&#xff1a;认知up吧 &#xff08;目前正在带领大家一起提升认知&#xff0c;感兴趣可以来围观一下&#xff09; &#x1f383;知识星球&#xff1a;【认知up吧|成长|副业】介绍 ❤️感谢大家点赞&#x1f44d;&…