前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >C++ new 与 delete 的使用规范

C++ new 与 delete 的使用规范

作者头像
恋喵大鲤鱼
发布于 2022-11-29 13:20:20
发布于 2022-11-29 13:20:20
94800
代码可运行
举报
文章被收录于专栏:C/C++基础C/C++基础
运行总次数:0
代码可运行

文章目录

C++ 的动态内存管理是通过 new 和 delete 两个操作来完成的,即用 new 来申请空间,用 delete 来释放空间。在使用 new 和 delete 时,注意以下原则。

1.new 与 delete 需一一对应

用 new 操作申请空间,如果申请成功,必须在以后的某个时刻用 delete 释放该空间,既不能忘记释放,也不能多次释放。前者会引起内存泄露,后者会引起运行时错误。

如下面的程序:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <iostream>
using namespace std;

int main() {
	int *p;
	p=new int(3);
	if (p) {
		delete p;
	}
	delete p;
	return 0;
}

以上程序对指针p所指向的空间进行两次释放,这种内存错误对 C++ 程序危害极大,也是很多人对 C++ 忘而却步的原因。多次释放同一块内存空间,并不一定立即引起程序运行错误,也不一定会导致程序运行的崩溃,这跟具体的编译器实现有关。但是,多次释放同一块内存空间绝对是一个编程错误,这个编程错误可能会在其后的某个时刻导致其他的逻辑错误的发生,从而给程序的调试和纠错带来困难。

考察如下程序:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <iostream>
using namespace std;

int main() {
	int *p, *q, *one;
	one = new int;
	if(one) {
		cout<<one<<endl;
	}
	delete one;
	p=new int(3);
	if(p) {
		cout<<p<<endl;
	}
	delete one;//假设这句语句是程序员不小心加上的
	q=new int(5);
	if(q) {
		cout<<q<<endl;
	}
	cout<<(*p)+(*q)<<endl;
	delete p;
	delete q;
}

程序通过编译,运行结果如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
003289A0
003289A0
003289A0
10

程序运行过程中会产生中断。从程序的输出可以看出,在将指针one所指向的空间释放后,为指针p申请的空间就是原来one所指向的空间。由于不小心在为p分配空间之后再次使用了delete one,导致q申请到的空间就是原来p所申请的空间,这样赋给*q的值就改写了原来p所指向的单元的值,导致最后输出结果为10。由此可知,多次释放同一块内存空间,即使不导致程序运行中断,也会破坏环境,使指针与所对应的空间的隶属关系出现混乱,从而导致逻辑错误。在大型程序设计中,这种逻辑错误的查找会变得十分费时费力。

**注意:**当指针 p 的值为 NULL 时,多次使用 delete p 并不会带来麻烦,因为释放空指针的空间实际上不会导致任何操作。所以,将“不用”的指针设置为 NULL 是一个好的编程习惯。

2.new[] 与 delete[] 需一一对应

在申请对象数组时,需要使用new[]运算符,与之对应,释放对象数组时,需要使用delete[]运算符。这一点与C语言有所区别,C中无论申请单个还是多个对象,均使用 malloc()/free() 函数。首先看一下 delete 与 delete[] 运算符的区别。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Test {
public:
	Test() { cout<<"ctor"<<endl; }
	~Test() { cout << "dtor" << endl; }
};

// segment1
Test* pArray1 = new Test[3];
delete pArray1;

// segment2
Test* pArray2 = new Test[3];
delete[] pArray2;

其中代码片段segment1运行结果如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ctor
ctor
ctor
dtor

segment2运行结果如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ctor
ctor
ctor
dtor
dtor
dtor

可以看出,delete 与 delete[] 区别在于释放对象数组时,delete 只调用了一次析构函数,delete[] 调用了三次析构函数,完成了对象数组的释放。实际上,在使用 new 和 new[] 申请内存空间时,会申请一段额外的内存来保存用户申请的内存空间大小,元素个数等信息。当使用delete[]释放内存空间时,会逐个调用对象的析构函数并完成最终的内存空间的释放。使用 delete 释放对象数组时,则只会调用单个对象的析构函数,造成内存泄漏。符号[]告诉编译器,在 delete 一块内存时,先去获取内存保存的元素个数,然后一一清理。所以使用 delete 释放 new[] 申请的内存空间和使用 delete[] 释放 new 申请的内存空间都是错误的做法。

具体使用时,需要注意以下两点: (1)对于内置数据类型,因为没有构造和析构函数,所以使用delete和delete[]的效果是一样的。比如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int* pDArr = new int[3];
//processing code
delete pDArr;	// 等同于 delete[] pDArr

对于内置数据类型,虽然可以使用delete完成对象数组内存空间的释放,但是为了保证代码的可读性,建议使用delete[]来完成。所以,new[] 与 delete[] 使用时应一一对应。

(2)对于经常使用 typedef 的程序员来说,很容易 new[] 与 delete 的混用,例如有如下操作:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
typedef int Height[NUM];
int* pHeight = new Height;

这个情况应该使用 delete 还是 delete[] 呢?答案如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
delete	pHeight;	// wrong,但容易错误地使用 delete。
delete[] pHeight;	// right。

为了避免出现上面的错误,建议不要对数组使用 typedef,或者采用 STL 中的 vector 代替数组。

3.构造函数中的 new/new[] 与析构函数的中 delete/delete[] 需一一对应

当类的成员中有指针变量时,在构造函数中用new申请空间并且在析构函数中用delete释放空间是一种标准的、安全的做法。

例如下面的程序:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <iostream>
using namespace std;

class Student {
	char* name;
public:
	Student() {
		cout<<"Default constructor"<<endl;
	}
	Student(char*);
	~Student();
};

Student::Student(char*s) {
	// Student(); // 此句运行时报错,构造函数不能调用其他构造函数。
	cout<<"In constructor,allocating space"<<endl;
	name=new char[strlen(s)+1];
	strcpy(name,s);
	cout<<"name:"<<name<<endl;
}

Student::~Student() {
	cout<<"In destructor, free space"<<endl;
	delete name;
}

int main() {
	Student s1("张三");
}

程序运行输出:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
In constructor,allocating space
name:张三
In destructor, free space

由于任何一个对象,其构造函数只调用一次,其析构函数也只调用一次,这样就能保证运行时new和delete操作是一一对应的,也就保证了内存管理的安全性。

在 C++ 中,一个构造函数不能调用本类的另一个构造函数,其原因就是为了防止构造函数的相互调用打破了内存申请与释放之间的这种对应关系。


参考文献

C++高级进阶教程.陈刚.P260-264 编写高质量代码改善C++程序的150个建议.李健.P69-71

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-09-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
FAQ_全志平台Tina3.0.7 RXXX cowbell方案启动时cpufreq报错且无法生成cpufreq节点
FAQ_全志平台Tina3.0.7 RXXX cowbell方案启动时cpufreq报错且无法生成cpufreq节点
阿志小管家
2024/02/02
1780
全志平台Tina系统htol测试
HTOL 高温使用寿命测试(High Temperature Operating Life)
阿志小管家
2024/02/02
2280
Tina_Linux_Key_快速配置使用指南
Allwinner 平台支持三种不同类型的Key:GPIO-Key,ADC-Key,AXP-Key。其中,GPIOKey又包括普通的gpio 按键和矩阵键盘。
韦东山
2023/02/25
2.4K0
Tina_Linux_Key_快速配置使用指南
全志T113双核异构处理器的使用基于Tina Linux5.0——异构双核通信的具体实现
本章节以SBC-T113S4主板的TinaLinux为例,介绍异构双核通信的实现。该方法也同样适用于T113i平台。 本章节主要涉及到Tina Linux内核的配置、Tina Linux文件系统(openwrt)的配置、Freertos的配置。其中Tina Linux内核的配置包括设备树的配置及相关内核驱动及协议的配置;Tina Linux文件系统(openwrt)的配置包括异构双核通信测试程序和小核C906终端的配置;Freertos的配置包括通信协议的配置。
阿志小管家
2024/11/21
2750
全志T113双核异构处理器的使用基于Tina Linux5.0——异构双核通信的具体实现
全志D1-H Tina Linux LEDC开发指南
在tina 根目录下,执行makekernel_menuconfig,配置路径如下:
一牛网论坛
2022/12/13
1.7K0
荔枝派Zero(全志V3S) tftp下载 kernel 和 nfs 挂载文件系统
传输文件每次都插拔 SD 卡太麻烦了,还是使用网线传输文件比较快,借此机会讲述一下 通过 tftp下载 kernel 和 nfs 挂载文件系统
Gnep@97
2023/08/10
9271
荔枝派Zero(全志V3S) tftp下载 kernel 和 nfs 挂载文件系统
解决LicheeRV 86 Panel在tina2.0配置lcd GPIO引脚及colorbar闪屏的问题
设备树修改参考了https://github.com/Tina-Linux/tina-d1x-lichee-rv和sipeed提供的licheerv_d1_compile。
阿志小管家
2024/02/02
5870
解决LicheeRV 86 Panel在tina2.0配置lcd GPIO引脚及colorbar闪屏的问题
MQ-Quad 全志H616 主线内核编译调试记录(u-boot、kernel、buildroot)
/arch/arm64/boot/dts/allwinner/sun50i-h616-orangepi-zero2.dts
阿志小管家
2024/02/02
7960
全志 Tina Linux 系统资源配置 开发指南 支持百问网T113 D1-H哪吒 DongshanPI-D1s V853-Pro等开发板
Tina Linux SDK的根目录下,执行make menuconfig命令可进入Tina Linux的配置界面。
韦东山
2022/12/28
9.4K0
全志 Tina Linux 系统资源配置 开发指南 支持百问网T113 D1-H哪吒 DongshanPI-D1s V853-Pro等开发板
全志 Tina Linux 存储介质切换:eMMC,SPI NAND,SPI NOR,SD Card,SD NAND
SDK切换存储介质需要修改board.dts、sys_config.fex、内核配置、TINA系统配置。另外,在spinor 存储介质下,通过 u-boot-sun8iw21p1.bin 进行烧录,u-boot-spinor-sun8iw21p1.bin 启动,使用sys_partition_nor.fex作为分区表。在非spinor介质(spinand、emmc、sdnand),通过u-boot-sun8iw21p1.bin进行烧录和启动,使用sys_partition.fex作为分区表。下文将介绍spinor切换spinand、spinand切换spinor、spinor切换emmc、spinor切换sdnand四种切换方式。
阿志小管家
2024/02/02
7580
TinaSDKV2.0 Kernel基本开发
可以在tina-sdk source后,在tina-sdk任意目录内执行 cconfigs命令直接切换到板级 Linux 设备树 和 配置文件目录。
韦东山
2024/08/19
1810
TinaSDKV2.0 Kernel基本开发
Linux SPI 开发指南
SPI 是一种高速、高效率的串行接口技术。通常由一个主模块和一个或多个从模块组成,主模块选择一个从模块进行同步通信,从而完成数据的交换,被广泛应用于 ADC、LCD 等设备与 MCU 之间。全志的 spi 控制器支持以下功能:
韦东山
2023/02/25
9.4K0
Linux SPI 开发指南
FAQ_全志平台Tina系统改用gpt分区表后系统启动异常问题解决方法(Waiting for root device /dev/nand0p3...)
FAQ_全志平台Tina系统改用gpt分区表后系统启动异常问题解决方法(Waiting  for  root  device  /dev/nand0p3...)
阿志小管家
2024/02/02
2500
Linux RTC 开发指南
介绍Linux 内核中RTC 驱动的适配和DEBUG 方法,为RTC 设备的使用者和维护者提供参考。
韦东山
2023/02/25
1.8K0
Linux RTC 开发指南
全志平台Tina系统编译安全固件的方法
3. ./scripts/createkeys生成一个key放到out目录对应的方案的路径下
阿志小管家
2024/02/02
3270
全志平台Tina系统编译安全固件的方法
Tina-SDK开发
Tina-SDKV2.0源码网盘链接:https://pan.baidu.com/s/13uKlqDXImmMl9cgKc41tZg?pwd=qcw7
韦东山
2024/08/24
4710
Tina-SDK开发
Linux TWI开发指南
介绍 Sunxi 平台上 TWI 驱动接口与调试方法,为 TWI 模块开发提供参考。
韦东山
2023/02/25
2.6K0
Linux TWI开发指南
全志V851S开发版无法正常挂载TF卡,sdc0、sdc1报错
硬件设备及镜像 主板为:Yuzuki Lizard V851S开发板 宿主机环境:ubuntu 22.04 SDK版本:Yuzukilizard的github上的Docker镜像 img为:github上Yuzukilizard释放的镜像:[01]v851s_linux_lizard_uart0_2022_12_29.img v851s_linux_lizard_uart0_2022_12_29.img
阿志小管家
2024/02/02
3660
快速启用开发板
将开发板配套的两根typec线,一根 直接连接至 开发板 OTG烧录接口 另一头连接至电脑的USB接口,开发板默认有系统,接通otg电源线就会通电并直接启动。
韦东山
2024/08/27
1560
快速启用开发板
全志D1开发板 XR829蓝牙 Can‘t get device info: No such device 自我分析及解决方案
想用D1开发板设计一个小电脑,搭配蓝牙鼠标键盘远程控制其他设备。因此需要用到XR829的蓝牙部分,现在的问题是:
阿志小管家
2024/02/02
3360
全志D1开发板 XR829蓝牙 Can‘t get device info: No such device 自我分析及解决方案
推荐阅读
相关推荐
FAQ_全志平台Tina3.0.7 RXXX cowbell方案启动时cpufreq报错且无法生成cpufreq节点
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验