Effective C++条款16——成对使用new和delete时要采取相同形式(资源管理)

news/2024/11/8 20:45:42/

以下动作有什么错?

std::string* stringArray = new std::string[100];
// ...
delete stringArray;

每件事看起来都井然有序。使用了new,也搭配了对应的 delete。但还是有某样东西完全错误:你的程序行为不明确(未有定义)。最低限度,stringArray所含的100个string对象中的99个不太可能被适当删除,因为它们的析构函数很可能没被调用。

当你使用new(也就是通过new动态生成一个对象),有两件事发生。第一,内存被分配出来(通过名为operator new的函数,见条款49和条款51)。第二,针对此内存会有一个(或更多)构造函数被调用。当你使用delete,也有两件事发生:针对此内存会有一个(或更多〉析构函数被调用,然后内存才被释放(通过名为operator delete的函数,见条款51)。delete的最大问题在于:即将被删除的内存之内究竟存有多少对象﹖这个问题的答案决定了有多少个析构函数必须被调用起来。

实际上这个问题可以更简单些:即将被删除的那个指针,所指的是单一对象或对象数组?这是个必不可缺的问题,因为单一对象的内存布局一般而言不同于数组的内存布局。更明确地说,数组所用的内存通常还包括“数组大小”的记录,以便delete知道需要调用多少次析构函数。单一对象的内存则没有这笔记录。你可以把两种不同的内存布局想象如下,其中n是数组大小;

当然啦,这只是个例子。编译器不需非得这么实现不可,虽然很多编译器的确是这样做的。

当你对着一个指针使用delete,唯一能够让delete知道内存中是否存在一个“数组大小记录”的办法就是:由你来告诉它。如果你使用delete 时加上中括号(方括号),delete便认定指针指向一个数组,否则它便认定指针指向单对象:

std::string* stringPtr1 = new std::string;
std::string* stringPtr2 = new std::string[100];
// ...
delete stringPtr1;        // 删除一个对象
delete [] stringPtr2;     // 删除一个由对象组成的数组

如果你对stringPtr1使用"delete []”形式,会发生什么事?结果未有定义,但不太可能让人愉快。假设内存布局如上,delete会读取若干内存并将它解释为“数组大小”,然后开始多次调用析构函数,浑然不知它所处理的那块内存不但不是个数组,也或许并未持有它正忙着销毁的那种类型的对象。

如果你没有对stringPtr2使用"delete []"形式,又会发生什么事呢?唔,其结果亦未有定义,但你可以猜想可能导致太少的析构函数被调用。犹有进者,这对内置类型如int者亦未有定义(甚至有害),即使这类类型并没有析构函数。

游戏规则很简单:如果你调用new时使用[],你必须在对应调用delete时也使用[ ]。如果你调用new时没有使用[],那么也不该在对应调用delete时使用[]。

当你撰写的class含有一个指针指向动态分配内存,并提供多个构造函数时,.上述规则尤其重要,因为这种情况下你必须小心地在所有构造函数中使用相同形式的new将指针成员初始化。如果没这样做,又如何知道该在析构函数中使用什么形式的delete呢?

这个规则对于喜欢使用typedef的人也很重要,因为它意味typedef的作者必须说清楚,当程序员以new创建该种typedef类型对象时,该以哪一种delete形式删除之。考虑下面这个typedef:

typedef std::string AddressLines[4];        // 每个人的地址由4行// 每行是一个string

由于AddressLines是个数组,如果这样使用new:

std::string* pal = new AddressLines;    // 注意,"new AddressLines"返回一个// string*,就像"new string[4]"一样

那就必须匹配“数组形式”的delete:

delete pal;            // 行为未有定义
delete [] pal;         // 很好

为避免诸如此类的错误,最好尽量不要对数组形式做typedefs 动作。这很容易达成,因为C++创ET主加你可以将本例的AddressLines定义为"由 Stitng数组的需求降至几乎为零。例如你可以将本例的AddressLines定义为“由strings组成的一个vector”,也就是其类型为vector<string>。

请记住

  • 如果你在new表达式中使用[],必须在相应的celete表达式中也使用[]。如果你在new表达式中不使用[],一定不要在相应的delete表达式中使用[ ]。

http://www.ppmy.cn/news/1059828.html

相关文章

Modbus通信协议详解

Modbus 协议是应用于电子控制器上的一种通用语言。通过此协议&#xff0c;控制器相互之间、控制器经由网络&#xff08;例如以太网&#xff09;和其它设备之间可以通信。它已经成为一通用工业标准。有了它&#xff0c;不同厂商生产的控制设备可以连成工业网络&#xff0c;进行集…

深入理解线性回归模型的评估与优化方法

文章目录 &#x1f340;引言&#x1f340;模型评估方法&#x1f340;均方误差&#xff08;MSE&#xff09;&#x1f340;均方根误差&#xff08;RMSE&#xff09;&#x1f340;绝对平均误差&#xff08;MAE&#xff09;&#x1f340;模型优化策略&#x1f340;特征工程&#x1…

一种IDEA疑难杂症的解决办法

解决办法 重启IDEA 针对于IDEA各种羡慕解析&#xff0c;运行时问题&#xff0c;但是无法通过搜索引擎得到答案的问题请试试此方法。 删除根目录下[.idea]文件夹后重启 此文件夹为idea首次导入项目时根据项目情况自动生成的配置文件。方便idea下次更快的解析项目。但是某些情…

C++非类型的模板参数(四)

在开发过程中&#xff0c;尤其在使用容器的时候&#xff0c;虽然其会自动的管理内存&#xff0c;但是在开发的过程中最好限制其大小&#xff0c;所以这里会通过一个非类型的模板参数来进行控制&#xff1b; 这里通过一维数组来进行说明 示例 /** brief: class complates* co…

车联网技术介绍

上图是目前车联网架构图&#xff0c;基于“云-管-端”的车联网系统架构以支持车联网应用的实现&#xff0c; “云”是指 V2X 基础平台、高基于精度定位平台等基础能力&#xff0c;可实现车辆动态厘米级定位&#xff0c;这将满足现阶段以及未来车联网应用场景的定位精度需求。 “…

比特币是怎么回事?

比特币是怎么回事&#xff1f; 一句话描述就是&#xff0c;初始化几个比特币&#xff0c;申请成为矿工组织&#xff0c;发生交易时抢单记账成功可以比特币奖励&#xff0c;随着比特币数量的增加&#xff0c;奖励越来越少。怎么记账成功呢&#xff0c;通过交易信息幸运数字哈希…

RTSP流媒体服务器EasyNVR视频平台设备通道时间与服务器录像时间不一致的问题解决步骤

EasyNVR平台优秀的视频能力在于通过RTSP/ONVIF协议&#xff0c;将前端接入设备的音视频资源进行采集&#xff0c;并转码成适合全平台、全终端分发的视频流格式&#xff0c;包括RTMP、RTSP、FLV、HLS、WebRTC等格式。平台已经在智慧水利、智慧工厂、智慧校园、智慧仓储等场景中应…

Vue项目:项目初始化+项目配置+项目集成

目录 初始化项目 创建项目 下载node_modules 运行测试 项目启动&#xff0c;自动打开 项目配置 配置eslint 安装eslint 生成配置文件: .eslint.cjs 安装Vue3环境代码校验插件 修改 .eslint.cjs 配置文件 为eslint生成 .eslintignore 忽略文件 为eslint添加运行脚…