Effective C++ 学习笔记 条款16 成对使用new和delete时要采取相同形式
以下动作有什么错?
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; // 很好
为避免诸如此类的错误,最好尽量不要对数组形式做typedef动作。这很容易达成,因为C++标准程序库(条款54)含有string、vector等templates,可将数组的需求降至几乎为零。例如你可以将本例的AddressLines定义为“由string组成的一个vector”,即其类型为vector<string>。
请记住:
如果你在new表达式中使用[],必须在相应的delete表达式中也使用[]。如果你在new表达式中不使用[],一定不要在相应的delete表达式中使用[]。
相关文章:
Effective C++ 学习笔记 条款16 成对使用new和delete时要采取相同形式
以下动作有什么错? std::string *stringArray new std::string[100]; // ... delete stringArray;每件事看起来都井然有序,使用了new,也搭配了对应的delete。但还是有某样东西完全错误:你的程序行为未定义。至少,str…...
PokéLLMon 源码解析(四)
.\PokeLLMon\poke_env\exceptions.py """ This module contains exceptions. """# 定义一个自定义异常类 ShowdownException,继承自内置异常类 Exception class ShowdownException(Exception):"""This exception is …...
区块链基础知识01
区块链:区块链技术是一种高级数据库机制,允许在企业网络中透明地共享信息。区块链数据库将数据存储在区块中,而数据库则一起链接到一个链条中。数据在时间上是一致的,在没有网络共识的情况下,不能删除或修改链条。 即&…...
YOLOv9(2):YOLOv9网络结构
1. 前言 本文仅以官方提供的yolov9.yaml来进行简要讲解。 讲解之前,还是要做一些简单的铺垫。 Slice层不做任何的操作,纯粹是做一个占位层。这样一来,在parse_model时,ch[n]可表示第n层的输出通道。 Detect和DDetect主要区别还…...
提取b站字幕(视频字幕、AI字幕)
提取b站字幕(视频字幕、AI字幕) 1. 打开视频 2. 按 F12 进行开发者界面 视频自己的紫米输入的是 json,如果是AI字幕则需要输入 ai_subtitle 3. 进入这个网址:https://www.dreamlyn.cn/bsrt...
JAVA程序员如何快速熟悉新项目?
文章目录 Java程序员快速熟悉一个新项目的步骤通常包括以下几个方面:实例展示:Java程序员加入新项目时可能遇到的技术难题及其解决方案包括: Java程序员快速熟悉一个新项目的步骤通常包括以下几个方面: 理解项目背景和目标&#x…...
慢sql优化记录1
慢sql为: select count(*) from t_wf_process p left join t_wf_core_dofile dofile on p.wf_instance_uid dofile.instanceid join zwkj_department d on p.userdeptid d.department_guid ,t_wf_core_item i,wf_node n where (p.IS_DUPLICATE ! true or p.IS_DU…...
堆和堆排序
堆排序是一种与插入排序和并归排序十分不同的算法。 优先级队列 Priority Queue 优先级队列是类似于常规队列或堆栈数据结构的抽象数据类型(ADT)。优先级队列中的每个元素都有一个相关联的优先级key。在优先级队列中,高优先级的元素优先于…...
STM32 | 零基础 STM32 第一天
零基础 STM32 第一天 一、认知STM32 1、STM32概念 STM32:意法半导体基于ARM公司的Cortex-M内核开发的32位的高性能、低功耗单片机。 ST:意法半导体 M:基于ARM公司的Cortex-M内核的高性能、低功耗单片机 32:32位单片机 2、STM32开发的产品 STM32开发的产品&a…...
day16_购物车(添加购物车,购物车列表查询,删除购物车商品,更新选中商品状态,完成购物车商品的全选,清空购物车)
文章目录 购物车模块1 需求说明2 环境搭建3 添加购物车3.1 需求说明3.2 远程调用接口开发3.2.1 ProductController3.2.2 ProductService 3.3 openFeign接口定义3.3.1 环境搭建3.3.2 接口定义3.3.3 降级类定义 3.4 业务后端接口开发3.4.1 添加依赖3.4.2 修改启动类3.4.3 CartInf…...
基于Spring Boot的图书个性化推荐系统 ,计算机毕业设计(带源码+论文)
源码获取地址: 码呢-一个专注于技术分享的博客平台一个专注于技术分享的博客平台,大家以共同学习,乐于分享,拥抱开源的价值观进行学习交流http://www.xmbiao.cn/resource-details/1765769136268455938...
libevent源码解析:定时器事件(三)
文章目录 前言一、用例小根堆管理定时器事件小根堆和链表管理定时器事件区别 二、基本数据结构介绍结构体成员分析小根堆和链表common_timeout图示 三、源码分析小根堆管理定时器事件event_newevent_addevent_dispatch 链表common_timeout管理定时器事件event_base_init_common…...
3D资产管理
3D 资产管理是指组织、跟踪、优化和分发 3D 模型和资产以用于游戏、电影、AR/VR 体验等各种应用的过程。 3D资产管理也称为3D内容管理。 随着游戏、电影、建筑、工程等行业中 3D 内容的增长,实施有效的资产管理工作流程对于提高生产力、减少错误、简化工作流程以及使…...
鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:Blank)
空白填充组件,在容器主轴方向上,空白填充组件具有自动填充容器空余部分的能力。仅当父组件为Row/Column/Flex时生效。 说明: 该组件从API Version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 子组件…...
【手游联运平台搭建】游戏平台的作用
随着科技的不断发展,游戏行业也在不断壮大,而游戏平台作为连接玩家与游戏的桥梁,发挥着越来越重要的作用。游戏平台不仅为玩家提供了便捷的游戏体验,还为游戏开发者提供了广阔的市场和推广渠道。本文将从多个方面探讨游戏平台的作…...
手把手教会你 - StreamAPI基本用法
1. 简介 目前响应式编程的学习中很多时候都用到了Lambda表达式和StreamAPI,那么今天就在这里记录一下一些最基本的使用方法。 StreamAPI中引入了流的概念,其将集合看作一种流,流在管道中传输(动态的),可以…...
和为K的子数组
题目: 使用前缀和的方法可以解决这个问题,因为我们需要找到和为k的连续子数组的个数。通过计算前缀和,我们可以将问题转化为求解两个前缀和之差等于k的情况。 假设数组的前缀和数组为prefixSum,其中prefixSum[i]表示从数组起始位…...
Redis:java中redis的基本使用(springboot)
文章目录 springboot中使用redisspringboot 连接 redis三种方式导入依赖增删改查小练习 springboot中使用redis springboot 连接 redis三种方式 jedis (redis官方提供的)springboot自带的redisson (基于jedis优化的,性能最好,使…...
微型计算机技术
摘要:微型计算机是通用计算机的一个重要发展分支,自1981年美国IBM公司推出第一代商用微型计算机以来,微型计算机迅速进入社会各个领域,且技术不断更新、产品快速换代,已成为人们工作和生活中不可缺少的基本工具。 一、微型计算机技术发展历史 1.第一代微处理器(19…...
mysql下载教程
什么是mysql MySQL是一种开源的关系型数据库管理系统,由瑞典MySQL AB公司开发,现在由Oracle公司维护。MySQL支持多个操作系统,包括Linux、Windows、macOS等。它是一种客户端/服务器模式的数据库,提供高效、可靠、稳定的数据存储和…...
【电赛实战利器】基于STM32F4与协方差修正的全数字锁相放大器设计与实测
1. 为什么你需要一个全数字锁相放大器? 在电子设计竞赛或者精密测量项目中,微弱信号检测总是让人头疼。想象一下,你要从一堆嘈杂的噪音中找出一个微弱的正弦波信号,就像在喧闹的菜市场里听清远处朋友的耳语。传统模拟锁相放大器需…...
7天打造智能助理:OpenClaw+Qwen3-VL:30B飞书开发周计划
7天打造智能助理:OpenClawQwen3-VL:30B飞书开发周计划 1. 为什么选择这个组合? 去年冬天,我偶然在GitHub上发现了OpenClaw这个项目。当时我正在为团队寻找一个既能处理日常办公自动化,又能理解图片内容的智能助手方案。传统的RP…...
COMSOL数值模拟:N2和CO2混合气体在THM热流固三场耦合下增强瓦斯抽采
COMSOL数值模拟,实现N2和CO2混合气体在THM热流固三场耦合情况下增强瓦斯(煤层气抽采)煤层气抽采效率提升这事儿,最近在实验室搞了个骚操作——往煤层里怼氮气和二氧化碳的混合气。说人话就是拿这俩气体当开塞露,把卡在…...
工业自动化实战:三大品牌伺服驱动器IO与串口引脚接线全解析
1. 伺服驱动器接线基础:为什么IO与串口引脚如此重要 第一次接触伺服驱动器时,我被密密麻麻的接线端子吓到了。后来才发现,只要理解几个核心引脚的功能,剩下的都是举一反三。伺服驱动器的IO和串口引脚就像机器的"神经系统&quo…...
解锁新可能:ArkData 在智能穿戴设备中的应用
解锁新可能:ArkData 在智能穿戴设备中的应用随着人们对健康生活的重视,智能穿戴设备愈发普及。这些设备能够实时收集心率、步数、睡眠等健康数据,为人们的健康管理提供重要参考。在这一背景下,如何高效管理和利用这些健康数据成为…...
用Logisim搞定六进制计数器:从真值表到同步置数/异步清零的保姆级布线教程
用Logisim搞定六进制计数器:从真值表到同步置数/异步清零的保姆级布线教程 第一次在Logisim里搭建计数器电路时,看着那些密密麻麻的逻辑门和跳线,我盯着屏幕发呆了半小时——明明按照课本上的真值表连接,仿真时却总是卡在某个状态…...
Windows下OpenClaw安装指南:快速对接百川2-13B量化模型
Windows下OpenClaw安装指南:快速对接百川2-13B量化模型 1. 为什么选择OpenClaw百川2-13B组合 去年我在处理个人知识管理时,发现每天要重复执行大量机械操作:整理网页资料、归档PDF、生成日报。直到遇见OpenClaw这个能像人类一样操作电脑的A…...
使用现代 Java 技术栈构建企业级 AI 应用
使用现代 Java 技术栈构建企业级 AI 应用 引言 随着人工智能技术的快速发展,企业级 AI 应用的需求也迅速增长。Java 作为一门成熟的企业级编程语言,其生态系统在 AI 应用开发中扮演着重要角色。本文将探讨如何利用 Java 技术栈构建生产级 AI 应用&#x…...
新能源企业数字化转型:从“卖设备“到“卖服务“的服务管理实践
在"双碳"目标驱动下,新能源产业正经历从"投建"到"运营服务"的战略转型。光伏、风电、储能等设备遍布全国各地,售后服务与运维效率直接关系到发电收益与品牌口碑。 然而,很多新能源企业面临一个共同的困境&…...
从Flask裸奔到MCP标准落地:7步迁移指南+自动转换脚本(已验证支撑日均50万次Agent调用)
第一章:Python MCP 服务器开发模板概览与核心价值Python MCP(Model-Controller-Protocol)服务器开发模板是一套面向协议驱动微服务架构的轻量级开发框架,专为快速构建符合 MCP 规范的 AI 工具集成后端而设计。它抽象了协议适配、会…...
