C++ new和delete的使用

这篇具有很好参考价值的文章主要介绍了C++ new和delete的使用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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


前言

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

当然系统还给了我另一个提示:
c++new和delete的用法,C++,c,c++,开发语言,数据结构,linux,1024程序员节
最后画面突然黑了两下,系统短暂卡死!当然,我今天不是来给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版本,具体类似于下下面的样子:
c++new和delete的用法,C++,c,c++,开发语言,数据结构,linux,1024程序员节
意思就是,如果主存耗尽,或者由于其他原因不能正常操作,不抛出异常。

示例代码:

#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、文明交流,请勿谩骂。文章来源地址https://www.toymoban.com/news/detail-702519.html

到了这里,关于C++ new和delete的使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++(20):constexpr函数中可以成对的使用new/delete

    C++20前,constexpr函数中是不能使用new和delete的。 C++20进一步的放宽了限制,允许成对的使用new和delete。

    2024年02月08日
    浏览(37)
  • C++野指针(Wild Pointers)是什么?如何避免?如何正确地使用new和delete?

    C++野指针(Wild Pointers)是什么?如何避免? C++野指针(Wild Pointers)指的是那些指向无效内存地址的指针。野指针通常是由于内存管理不当导致的,比如未初始化的指针、指向已释放内存的指针、越界访问导致的指针等。野指针是非常危险的,因为它们可能引发未定义行为,

    2024年02月20日
    浏览(37)
  • 【ES6 Map数据结构】建议日常开发操作数组时使用 new Map

    算法使用 Map 还算是不少的,日常开发也可使用 new Map 替代某些数组操作,活到老学到老 JavaScript的对象 (Object) ,本质上是键值对的集合 (Hash结构) ,但是传统上只能用字符串当作键。这给它的使用带来了很大的限制 为了解决这个问题,ES6提供了Map数据结构。它类似于对

    2024年01月17日
    浏览(44)
  • C++ new delete

    可执行程序(进程) 的虚拟地址空间: 内核: 操作系统 栈区:函数的形参,非静态的局部变量,函数现场保护数据等等,栈是向下增长的。 共享库的内存映射区域:用于装载一个共享的动态内存库。用户可使用系统接口创建共享内存,做进程间通信。 堆区: 用于程序运行时动态内

    2024年02月08日
    浏览(40)
  • C++:new 和 delete

    个人主页 : 个人主页 个人专栏 : 《数据结构》 《C语言》《C++》 本篇博客作为C++:new 和 detele操作符的知识总结 注意:申请连续空间初始化时与数组初始化类似 注意:申请和释放单个元素空间,使用new 和 delete操作符,申请和释放连续的空间,使用new[] 和 delete[]。 注意:

    2024年02月07日
    浏览(34)
  • C++:new与delete

    其对自定义内省申请动态内存的操作是很简单的,我们直接看如下对自定义类型的操作。 区别于C语言的是, new来申请自定义空间,会去调用构造函数 delete删除也会主动调用析构函数。 因此有一种操作 类的内部禁用掉对应的operator new和operator delete那么,对这个类你就不能使

    2024年04月13日
    浏览(35)
  • C++ :内存管理 new&delete

    目录 内存区域划分 C++的动态内存的管理方式   new new的基本使用方法  【注意事项】  delete  【注意】 new和delete操作自定义类型  operator new 和 operator delete  【关于自定义类型new申请内存】 【原理】  【调用顺序】  【连续开辟空间问题】  malloc/free和new/delete的区别 【说明

    2024年02月22日
    浏览(44)
  • C++ new和delete详解

    说明: 栈又叫堆栈–非静态局部变量/函数参数/返回值等等,栈是向下增长的 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。(Linux课程如果没学到这块,现在只需要了解一下) 堆用于程序运行时动

    2024年02月09日
    浏览(36)
  • C++ || C/C++内存管理 | C++动态内存管理方式 | operator new/delete函数 | new和delete实现原理 | 定位new表达式 | 内存泄漏

    C/C++中程序内存区域大致划分六个部分: 内核空间 (用户代码不能读写)、 栈 (向下增长)、 内存映射段 (文件映射、动态库、匿名映射)、 堆 (向上增长)、 数据段 (全局数据、静态数据)、 代码段 (可执行代码、只读常量)。 各自内存区域功能 栈 ,又叫做堆栈

    2024年02月21日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包