C++ new和delete的使用

news2024/11/28 0:37:44

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、new和delete介绍
  • 二、简单使用
    • 1.new和delete
    • 2.自定义对象
    • 3.new[]和delete[]
    • 4.主存耗尽
    • 5.try&catch
    • 6.nothrow
    • 7.看下源代码


前言

new和delete是C++里非常重要的两个关键字,意味着从“自由存储(堆)”分配指定大小的内存和释放掉这些内存。这些用法哪怕初学者也会,但是今天要讲的不是这个。今天要讲的是使用中容易忽视的细节和可能引发的错误


一、new和delete介绍

首先,new和delete总是成对出现,顺序也不能错。一定是先new再delete。其次,new和delete是针对单个对象,还有new[]和delete[]针对数组。最后,我们先从最简单的使用开始,慢慢带入。

二、简单使用

1.new和delete

这段代码演示针对内置对象的使用。

代码如下(示例):

#include <iostream>
using namespace std;

void test_1(){
	auto* p = new int;
	delete p;
}

p是指向一块new出来的“自由存储”,delete负责回收掉这块存储。这应该是最简单的用法了,需要注意的是p是局部变量,出了作用域p就被回收了(p在栈里),如果你没有在这里delete p,那么这块存储就一直在,一直不能被回收,直到你的程序结束被系统回收。

当然这还不是p面临的所有问题,还有其他问题后面再说。

2.自定义对象

new和delete还可以操作自定义对象。

代码如下(示例):

#include <iostream>

using namespace std;

struct t{
    t()= default;//没有特殊操作
    explicit t(int){}//阻止隐式转换
};

void test_2() {
    auto* p = new t;//默认构造
    auto* q = new t(1);//带参构造
    delete p;
    delete q;
}

用法和内置类型最大的差别就是在构造函数上,构造函数分为有参和无参,new不同的构造函数会调用不同的构造函数,除此之外没有大差别,delete不区分有参和无参,会调用同一个“析构函数”,如果你在自定义对象里面又申请了自由存储,切记在析构里回收掉。

3.new[]和delete[]

和new与delete的组合差不多,请看代码:

#include <iostream>

using namespace std;

void test_3(){
    auto*p = new int[10];
    delete[] p;
}

唯一的差别就是new[]针对的是数组,delete后面必须要加上[]。

4.主存耗尽

上面说了test_1()里面还有一个bug:这个bug在平常使用中不会出现,只有特殊情况才会触发。这个错误我详细描述下:new出来的空间在RAM上,甚至可能还带上一些SWAP空间,这些空间是有限的,当空间不足的时候会抛出一个std::bad_alloc错误,这个异常你要把它抓住,要不然会导致程序异常终止。具体复现代码请看:

#include <iostream>

using namespace std;

void test_4(){
    for(;;)
        auto p = new int[8192];
}

我建议你复现之前保存下重要工作!我是在windows11上操作的,由于这个系统自带bug,我差点把它玩崩溃掉。
不出意外的话意外发生了:

terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc

当然系统还给了我另一个提示:
在这里插入图片描述
最后画面突然黑了两下,系统短暂卡死!当然,我今天不是来给windows11找bug的,测完这个之后操作系统感觉不太对劲,我重启了它。原因是主存耗尽,引发其它程序也不能正常运行。

这里有一个问题需要特别强调下:因为是模拟主存耗尽,而且是我的程序吃掉了绝大多数的主存,所以最后我的程序同时被操作系统检测到了“问题”,虽然在windows11上没有把我“杀死”,实测在Ubuntu上这么操作是会被操作系统直接杀死的。原因是:为了操作系统的稳定运行,会保留一部分主存给操作系统用,一旦出现主存耗尽的情况,操作系统会自主决定杀死一些“占用大”的程序来保证“自身”的运行。

所以,还有一种情况:本来主存就不足了,而我的程序同时也不是占用最大的那个,操作系统就可能决定不杀死我的程序,转而杀死其它占用大的程序。但是,不代表我们就安全了,我们还面临一个问题,那就是std::bad_alloc。这个问题怎么解决?

很多人可能没想过这个问题,而事实是绝大多数场景下你都不会面临这个问题。但是,凡事总有万一,如果你不处理这个问题,你的程序就提前终结,这肯定不是你想要的结果。

庆幸的是,C++标准给了我们解决方法,请看下面。

5.try&catch

没错,它闪亮登场了。只要是异常就归它管,这里的std::bad_alloc异常是派生自std::exception,我们只要抓住它就可以了。

请看示例:

#include <iostream>

using namespace std;

void test_5() {
    try {
        auto *p = new int;
        //...
        delete p;
    } catch (bad_alloc &e) {
        //...
    }
}

看起来很完美,唯一的缺点就是每次new都要try&catch,增加了繁琐性。有没有一个稍微简单的方法?请看下面:

6.nothrow

new和delete可以选择nothrow版本,具体类似于下下面的样子:
在这里插入图片描述
意思就是,如果主存耗尽,或者由于其他原因不能正常操作,不抛出异常。

示例代码:

#include <iostream>

using namespace std;

void test_6() {
    auto *p = new(nothrow) int;
    if(p){
		//...
	}
    delete p;
}

这里new只要加上nothrow参数指名不抛出异常,但也不代表一定申请成功,所以还需要if判断。写法没有try&catch那么臃肿,比较推荐这个方法。

原则上,我们无法知道哪一次new会导致这种问题,所以如果你不想有意外惊喜,又或者确实有这个需要的话,就做一些处理吧。

7.看下源代码

C++标准库的源代码对new&delete和new[]&delete[]分别做了重载。

// Macro for noexcept, to support in mixed 03/0x mode.
#ifndef _GLIBCXX_NOEXCEPT
# if __cplusplus >= 201103L
#  define _GLIBCXX_NOEXCEPT noexcept
#  define _GLIBCXX_NOEXCEPT_IF(...) noexcept(__VA_ARGS__)
#  define _GLIBCXX_USE_NOEXCEPT noexcept
#  define _GLIBCXX_THROW(_EXC)
# else
#  define _GLIBCXX_NOEXCEPT
#  define _GLIBCXX_NOEXCEPT_IF(...)
#  define _GLIBCXX_USE_NOEXCEPT throw()
#  define _GLIBCXX_THROW(_EXC) throw(_EXC)
# endif
#endif

//@{
/** These are replaceable signatures:
 *  - normal single new and delete (no arguments, throw @c bad_alloc on error)
 *  - normal array new and delete (same)
 *  - @c nothrow single new and delete (take a @c nothrow argument, return
 *    @c NULL on error)
 *  - @c nothrow array new and delete (same)
 *
 *  Placement new and delete signatures (take a memory address argument,
 *  does nothing) may not be replaced by a user's program.
*/
_GLIBCXX_NODISCARD void* operator new(std::size_t) _GLIBCXX_THROW (std::bad_alloc)
  __attribute__((__externally_visible__));
_GLIBCXX_NODISCARD void* operator new[](std::size_t) _GLIBCXX_THROW (std::bad_alloc)
  __attribute__((__externally_visible__));
void operator delete(void*) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__));
void operator delete[](void*) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__));
#if __cpp_sized_deallocation
void operator delete(void*, std::size_t) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__));
void operator delete[](void*, std::size_t) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__));
#endif
_GLIBCXX_NODISCARD void* operator new(std::size_t, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__, __alloc_size__ (1), __malloc__));
_GLIBCXX_NODISCARD void* operator new[](std::size_t, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__, __alloc_size__ (1), __malloc__));
void operator delete(void*, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__));
void operator delete[](void*, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__));

从源代码里可以看出:默认的delete和delete[]都是noexcept的,默认的new是可以throw的,当我们指定nothrow的时候它就调用noexcept那个版本了。这个特性是C++11(201103L)以后的版本支持的,切记!关于C++的版本代号请查询官方文档,这里不再赘述。

这里有个小插曲:函数声明为noexcept的特性由C++标准提供强保证,简而言之就是C++标准保证声明为new(nothrow)的函数一定不会抛出异常,可以放心大胆地使用。有意思的是,我们自己也可以把一个函数声明为noexcept的,编译器会对其进行优化,当然你也要保证这个函数一定不会抛出异常,假如抛出了会怎么办?你可以自己试一试。


总结
1、总体没什么难度,多注意下就不会出错。
2、有疑问,或者有不对的地方请在此留言,我可以在邮箱收到提醒邮件。
3、文明交流,请勿谩骂。

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

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

相关文章

【运维】服务器系统安装

目录 一、环境 二、ubuntu 三、启动u盘制作 Stage 1&#xff1a;下载balena&#xff0c;制作U盘启动工具 Stage 2&#xff1a;下载Ubuntu 系统镜像&#xff08;参考上一节&#xff1a;Ubuntu 22.04.2 LTS &#xff09; Stage 3&#xff1a;将镜像写入到U盘 四、设置开启…

6 面阿里、5 面字节、4 面腾讯,可算是入坑了····

8 年前&#xff0c;BAT 冲到了风口浪尖&#xff0c;美国上市的阿里成为中国体量最大的互联网公司&#xff0c;腾讯借助微信成为移动互联网的霸主&#xff0c;外企开始撤离中国&#xff0c;国企的光环也慢慢褪去。 到了近年&#xff0c;应届毕业生心中最炙手可热的公司换成了 T…

共话出海、布局全球,融云WICC2023 · 泛娱乐出海嘉年华广州收官!

&#xff08;移步公众号点击图片三折购买《社交泛娱乐出海作战地图》&#xff09; 6 月 2 日&#xff0c;“WICC 泛娱乐出海嘉年华”在广州成功举办&#xff0c;圆满收官。关注【融云全球互联网通信云】了解更多 本届嘉年华由高端峰会、圆桌会议、露营派对三部分组成&#xf…

资深8年测试,全链路压测与性能的优化详解,一文通透...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 什么是全链路压测…

了解CPU瓶颈原因,掌握代码优化、TOP命令及缓存技术,让服务器不再为性能瓶颈所困扰。

目录 前言&#xff1a; 一、CPU瓶颈原因分析 1. CPU使用率过高 2. 进程使用CPU过多 3. 磁盘I/O读写速度过慢 二、CPU瓶颈调优方案 1. 使用top命令查看CPU使用率 2. 优化程序设计 3. 使用缓存技术 总结&#xff1a; 前言&#xff1a; 在服务器运行过程中&#xff0c;…

干货 | 实战演练基于加密接口测试测试用例设计

如果接口测试仅仅只是掌握一些requests或者其他一些功能强大的库的用法&#xff0c;是远远不够的&#xff0c;还需要具有根据公司的业务以及需求去定制化一个接口自动化测试框架能力。所以在这个部分&#xff0c;会主要介绍接口测试用例分析以及通用的流程封装是如何完成的。 首…

GitHub 竟然有这些骚操作,真是涨姿势

GitHub 竟然有这些骚操作&#xff0c;真是涨姿势 GitHub&#xff0c;不用过多介绍。一个面向开源及私有软件项目的托管平台&#xff0c;因为只支持 git 作为唯一的版本库格式进行托管&#xff0c;故名 GitHub。 作为「全球最大的程序员“交友”社区」&#xff0c;程序员的你&am…

入门AI从谷歌这10门独立课程开始

​ 谷歌最近发布了一个名为"Generative AI learning path"的学习路径&#xff0c;该路径专为初学者设计&#xff0c;共包含10门独立课程。通过这个学习路径&#xff0c;初学者可以从基础概念开始学习&#xff0c;并逐步深入到更复杂的主题&#xff0c;帮助他们了解生…

NLP实战:调用Gensim库训练Word2Vec模型

目录 一、准备工作 1. 安装Gensim库 2. 对原始语料分词 二、训练Word2Vec模型 三、模型应用 1.计算词汇相似度 ​编辑 2. 找出不匹配的词汇 3. 计算词汇的词频 四、总结 &#x1f368; 本文为[&#x1f517;365天深度学习训练营]内部限免文章&#xff08;版权归 *K同学…

第7章:SpringMVC的HttpMessageConverter

1. HttpMessageConverter简介 ①HttpMessageConverter&#xff0c;报文信息转换器&#xff0c;将请求报文转换为java对象&#xff0c;或将java对象转换为响应报文 ②HttpMessageConverter提供了两个注解和两个类型 RequestBody,ResponseBody,RequestEntity,ResponseEntity …

深入详解CFS任务放置代码

一、前言 本文出现的内核代码来自Linux5.10.61&#xff0c;为了减少篇幅&#xff0c;我们对引用的代码进行了删减&#xff08;例如去掉了NUMA的代码&#xff0c;毕竟手机平台上我们暂时不关注这个特性&#xff09;&#xff0c;如果有兴趣&#xff0c;读者可以配合完整的源代码…

Spring Boot JAVA 统一返回的信息

现在的项目是前后端开发的居多&#xff0c;那么&#xff0c;后端的开发只需要返回相关的接口就行了。那么&#xff0c;我们怎么定义接口返回的数据&#xff0c;怎么使用 Spring Boot 来统一处理返回的信息呢&#xff1f; 开发环境如下&#xff1a; IntelliJ IDEA 2021.2.2 (U…

linuxOPS基础_linux权限管理

权限概述 什么是权限 ​ 在多用户计算机系统的管理中&#xff0c;权限是指某个特定的用户具有特定的系统资源使用权利。 在Linux 中分别有读、写、执行权限 \权限针对文件权限针对目录读r(read)表示可以查看文件内容&#xff1b;cat、less…表示可以(ls)查看目录中存在的文…

无人机+ AI 图像分析:里斯本大学高效检测林业害虫

内容一览&#xff1a;早期发现虫害对于因地制宜采取防控措施至关重要。尽管遥感技术可用于快速扫描大面积区域&#xff0c;但面对低强度信号或难以检测的物体&#xff0c;其效果并不尽如人意。因此&#xff0c;里斯本大学研究人员将无人机与 AI 图像分析相结合&#xff0c;在此…

如何对自动化测试工具选型?这4个因素必须要拿捏好

测试开发工程师再做自动化时避免不了针对每个工具进行选型。今天就来看下工具选型的方法。 一&#xff1a;工具选型方法 在作为一名自动化测试开发工程师时&#xff0c;选择合适的 Web 自动化工具非常关键。以下是一些评估和选择工具的依据和方法&#xff1a; 1.支持的语言和…

接口测试介绍以及用例编写

6.1 接口 6.1.1 接口概述 定义&#xff1a; 接口就是API&#xff08;Application Programming Interface&#xff0c;应用程序接口&#xff09;&#xff0c;是一个软件或服务对外提供的接口&#xff0c;别人只要调用这接口&#xff0c;而内部如何实现&#xff0c;不需要关心。…

608教室使用方法

一、教室平面图 608教室的布局如下&#xff0c;重要的设备已经在图中标出。总开关、一体机和机柜。   二、使用方法 2.1 房间机器上电 进门后首先走到“总开关位置”&#xff0c;将电匝闭合。 原来的开关如图所示&#xff0c;有3组开关&#xff0c;1号组开关用于控制插座、…

突破办公瓶颈,推荐5款电脑办公软件

1.AnyTXT AnyTXT是一款搜文件内容的第三方全文搜索工具&#xff0c;速度极快&#xff0c;基本秒出结果。而且右边可以直接预览文件&#xff0c;筛选&#xff0c;复制&#xff0c;翻译等功能丰富&#xff0c;完美替代自带的搜索。 2.Groupy 这是用于快速切换窗口的小型精致工…

微信支付_Native支付流程

介绍 商户后台系统先调用微信支付的Native下单接口&#xff0c;微信后台系统返回链接参数code_url&#xff0c;商户后台系统将code_url值生成二维码图片&#xff0c;用户使用微信客户端扫码后发起支付。微信支付-开发者文档 注意&#xff1a; code_url有效期为2小时&#xff0…

spring实例化bean整体流程

AbstractApplicationContext类的invokeBeanFactoryPostProcessors方法把所有bean变成了beanDefinition&#xff0c;然后在finishBeanFactoryInitialization方法中完成实例化。 实例化过程只分析单例 1.DefaultListableBeanFactory#preInstantiateSingletons方法 这个方法的功…