C++动态内存管理new,delete

C++动态内存管理new,delete

  • 1.C/C++内存分布
  • 2.C语言中的内存管理方式
  • 3.C++中的内存管理方式new,delete
    • 3.1C++中的内置类型new,delete
    • 3.2new,delete操作自定义类型
    • 3.3 new和delete匹配
  • 4. operator new与operator delete函数
    • 4.1new和delete底层实现
    • 4.2new和delete匹配,new []和delete []匹配
  • 5. 定位new表达式(placement-new)
    • 5.1定义
    • 5.2使用场景

所属专栏:C“嘎嘎" 系统学习❤️
🚀 >博主首页:初阳785❤️
🚀 >代码托管:chuyang785❤️
🚀 >感谢大家的支持,您的点赞和关注是对我最大的支持!!!❤️
🚀 >博主也会更加的努力,创作出更优质的博文!!❤️

1.C/C++内存分布

在学习C语言的时候我们有讲到过动态内存够管理部分C语言当中的动态内存管理在这一篇文章中我们也有详细了解到什么是动态内存,为什么要存在动态内存。

这里我们也做简单的回顾一下:

  • 在内存中为什么要存在内存区域划分呢?因为在我们向内存输入数据的时候,不同的数据有不同存储需求,而不同的内存区域就是为了满足对不同数据的存储需求而设计的。
    • 比如一些数据可能就是一个临时的数据,我们就可以存储到栈区。
    • 有些数据可能是动态使用的,需要时就创建,不需要是就销毁,就可以存到堆区。
    • 有些数据可能是要长期使用,整个程序期间使用,随时都可能需要使用的,这个时候就可以存放在静态区。
    • 也有一些数据是只读不修改的,比如常量,可执行代码,这些数据就可以存放在常量区。

我们可以用一张内存区域分布图来概括我们的内存:
在这里插入图片描述
说明:

  1. 栈又叫堆栈–非静态局部变量/函数参数/返回值等等,栈是向下增长的。
  2. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口
    创建共享内存,做进程间通信。
  3. 堆用于程序运行时动态内存分配,堆是可以上增长的。
  4. 数据段–存储全局数据和静态数据。
  5. 代码段–可执行的代码/只读常量。

下面我们来做一些判断内存区域的题型:

int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{static int staticVar = 1;int localVar = 1;int num1[10] = { 1, 2, 3, 4 };char char2[] = "abcd";const char* pChar3 = "abcd";int* ptr1 = (int*)malloc(sizeof(int) * 4);int* ptr2 = (int*)calloc(4, sizeof(int));int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);free(ptr1);free(ptr3);
}
  1. 选择题:
    选项 : A.栈 ;B.堆 ;C.数据段(静态区) ;D.代码段(常量区)
    1.globalVar在哪里?____ ;2.staticGlobalVar在哪里?____;

    3.staticVar在哪里?____ ;4.localVar在哪里?____;

    5.num1 在哪里?____;

    6.char2在哪里?____ ;7.* char2在哪里?___;

    8.pChar3在哪里?____ ;9.* pChar3在哪里?____;

    10.ptr1在哪里?____ ;11.* ptr1在哪里?____;

  2. 填空题:
    1.sizeof(num1) = ____;

    2.sizeof(char2) = ____;3. strlen(char2) = ____;

    4.sizeof(pChar3) = ____; 5.strlen(pChar3) = ____;

    6.sizeof(ptr1) = ____;

正确答案:

一:1-5:CCCAA,6-11:AAADAB
二:40,5,4,4/8,4,4/8

第一题的前5个因该都没什么问题,问题可能出现在6-11题,我们讲解一下;
在这里插入图片描述

2.C语言中的内存管理方式

在学习C语言的时候我们有讲到过动态内存够管理部分C语言当中的动态内存管理在这一篇文章中我们也有详细了解到什么是动态内存,为什么要存在动态内存,如果有对知识点模糊不清的可以回顾一下。

3.C++中的内存管理方式new,delete

  • C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因
    此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。

3.1C++中的内置类型new,delete

使用语法:

new + 类型(初始值)
delete + 类型

new + 类型[大小]
delete[] + 类型

int main()
{//开辟一个int类型的空间int* ptr1 = new int;//开辟一个int类型的空间并初始化为2int* ptr2 = new int(2);delete ptr1;delete ptr2;//开辟一个int类型的数组,大小为3int* ptr3 = new int[3];//开辟一个int类型的数组,大小为10,并初始化int* ptr4 = new int[10]{1,2,3,4};//没有被初始化的编译器会默认初始化为0delete[] ptr3;delete[] ptr4;return 0;
}

在这里插入图片描述
注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用
new[]和delete[],注意:匹配起来使用。

3.2new,delete操作自定义类型

class text
{
public:text(int val):_a(val){}
private:int _a;
};int main()
{//C语言中的malloctext* t1 = (text*)malloc(sizeof(text));//C++中的newtext* t2 = new text(1);free(t1);delete t2;return 0;
}

在这里插入图片描述

从上面malloc和new出来用法不一样之外没什么区别,唯一的区别就是malloc无法对自定义类型进行初始化,而new可以对自定义类型进行初始化,也就是说new做了两件事:开辟空间+构造函数。delete也做了两件事情:调用析构函数+释放空间。

3.3 new和delete匹配

class stack
{
public:stack(int capacity = 4){cout << "stack()" << endl;_a = new int[capacity];_size = 0;_capacity = capacity;}~stack(){cout << "~stack()" << endl;delete[] _a;_a = nullptr;_size = 0;_capacity = 0;}
private:int* _a;int _size;int _capacity;
};int main()
{stack* p1 = new stack(4);delete p1;return 0;
}

在这里插入图片描述

我们看一下上面一段代码,他的执行顺序什么呢?
在这里插入图片描述

  • 那如果我们把delete p1 换成 C语言中的free(p1)会怎么样呢?
    是不是free直接释放了对象的空间,而_a没有被释放啊,就会有内存泄漏。
    所以这里new一定要和delete匹配着使用。

4. operator new与operator delete函数

4.1new和delete底层实现

  • new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是
    系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过
    operator delete全局函数来释放空间。要注意的是operator new和operator delete不是重载 底层其实是封装了malloc和free。
  1. 内置类型
    如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:
    new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申 请空间失败时会抛异常,malloc会返回NULL。
  2. 自定义类型
    • new的原理
      1. 调用operator new函数申请空间
      2. 在申请的空间上执行构造函数,完成对象的构造
    • delete的原理
      1. 在空间上执行析构函数,完成对象中资源的清理工作
      2. 调用operator delete函数释放对象的空间
    • new T[N]的原理
      1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对
        象空间的申请
      2. 在申请的空间上执行N次构造函数
    • delete[]的原理
      1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
      2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释
        放空间

官方库给定的封装:

/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间
失败,尝试执行空 间不足应对措施,如果改应对措施用户设置了,则继续申请,否
则抛异常。
*/
void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{// try to allocate size bytesvoid* p;while ((p = malloc(size)) == 0) //最要注意的是这里,malloc开辟失败是返回0,而operate delete开辟失败不是返回0,而是抛异常。if (_callnewh(size) == 0){// report no memory// 如果申请内存失败了,这里会抛出bad_alloc 类型异常static const std::bad_alloc nomem;_RAISE(nomem);}return (p);
}
/*
operator delete: 该函数最终是通过free来释放空间的
*/
void operator delete(void* pUserData)
{_CrtMemBlockHeader* pHead;RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));if (pUserData == NULL)return;_mlock(_HEAP_LOCK); /* block other threads */__TRY/* get a pointer to memory block header */pHead = pHdr(pUserData);/* verify block type */_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));_free_dbg(pUserData, pHead->nBlockUse);__FINALLY_munlock(_HEAP_LOCK); /* release other threads */__END_TRY_FINALLYreturn;
}
/*
free的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)
  • 通过上述两个全局函数的实现知道,operator new 实际底层也是通过malloc来申请空间,如果
    malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施
    就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的。
  • 而且我么也可以发现 operator new(size_t size)的参数是size_t size是一个大小值,operator delete(void* pUserData)的参数是一个对象。

我们也可以从汇编的角度来看一看:

class text
{
public:text(int a = 0){_a = a;cout << "text()" << endl;}~text(){cout << "~text()" << endl;}
private:int _a;
};int main()
{text* p1 = new text;delete p1;text* p2 = (text*)operator new(sizeof(text));operator delete(p2);return 0;
}

在这里插入图片描述
这里只调用了一次构造和一次析构。

  • text* p1 = new text;
    在这里插入图片描述
  • delete p1;
    在这里插入图片描述
  • 这里从汇编的角度我们就很清楚的可以看到,为什么new会做两件事情:开空间+调用构造,delete会调用析构+释放空间。
    底层其实是调用了malloc和free,只不过是被封装过的maloc和free。

我们也可以这样理解:

new = operator new + 构造函数
delete = 析构函数 + operator delete

4.2new和delete匹配,new []和delete []匹配

class text
{
public:text(int a = 0){_a = a;cout << "text()" << endl;}~text(){cout << "~text()" << endl;}
private:int _a;
};int main()
{text* p1 = new text[10];delete[] p1;return 0;
  • 我们看一下new[]是怎么开辟空间的。首先text类的大小是4个字节,开辟10个就是40个字节。这里可以类比一下开辟数组,数组是一个连续的内存,这里同样也是,10个类的物理内存是连续的,也就是说底层只会调用一次malloc,但是会调用10次析构函数。
    在这里插入图片描述
  • 如果按照我么正常的计算,这里内存因该会开辟40个字节的空间。
    我们进入汇编查看一下:
    在这里插入图片描述
  • 这里从监视窗口中我们看到的却是44个字节,对开了4个字节。
    我们在到内存中看一看:
    在这里插入图片描述
  • 至于为什么要多开一个个数的空间呢?
    是为了delete[]准备的,我们知道,new[10]是调用一次底层的malloc加上调用了10次构造函数,而之所以会调用10次构造函数是因为我们告诉编译器我们要开辟10个类内存大小。但是delete[ ]我们是没有告诉他有多少个的,它也就不知道要析构多少次,所以这块多出来的空间是为了提供给delete[]告诉它我们开辟了多少空间,让它析构多少次。
    在这里插入图片描述
  • 所以如果我们把delete[]换成delete或者free会怎么样呢?
    程序会崩掉,因为如果直接用delete或者free,编译器会认为你要释放的是一个对象,也就是从上面的p1开始释放,没有从多开的那块空间开始释放,也就只释放了部分的空间,而我们知道free是不能释放部分内存的,所以程序会崩。
    这里就说明了,我们要养成好匹配的习惯。

new和delete匹配。
new[] 和delete[]匹配。

5. 定位new表达式(placement-new)

5.1定义

  • 定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
    使用格式:
    new (place_address) type或者new (place_address) type(initializer-list)
    place_address必须是一个指针,initializer-list是类型的初始化列表。

  • 问一个问题,构造函数能显示调用吗?
    答案是不能显示调用的,构造函数是创建对象时自动调用的。
    但是析构函数可以显示调用。
    所以C++中可以用定位new来显示调用构造函数

class text
{
public:text(int a = 0){_a = a;cout << "text()" << endl;}~text(){cout << "~text()" << endl;}
private:int _a;
};int main()
{text* p1 =(text*)operator new(sizeof(text));new(p1)text(1);//上面两个加起来就等于text* p1 = new text;p1->~text();operator delete(p1);//上面两个加起来就等于delete p1;delete[] p1;return 0;
}

5.2使用场景

  • 使用场景:
    定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如
    果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

反转链表的实现

题目描述&#xff1a; 给出一个链表的头节点&#xff0c;将其反转&#xff0c;并返回新的头节点 思路1&#xff1a;反转地址 将每个节点里的地址由指向下一个节点变为指向前一个节点 定义三个结构体指针n1,n2,n3,n1表示改后指针的地址&#xff0c;n2表示要修改结构体里next的…

PT读spef报PARA-006如何解决?

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧? 拾陆楼知识星球入口 There are multiple causes that can trigger PARA-006 errors. Here is a checklist. 1) SPEF reading order Functionally, the parasitic files can be read in any order. For best stitching…

百度飞桨(张江)人工智能产业赋能中心入驻申请

中心如何赋能 百度飞桨&#xff08;张江&#xff09;人工智能产业赋能中心是浦东“大企业开放创新中心计划”首批企业代表&#xff0c;百度和张江集团将联合为入驻初创企业及生态合作伙伴提供以下服务&#xff1a; 降低AI使用门槛 通过“百度飞桨人工智能公共技术平台”&#x…

解决git与huggingface项目下载速度慢或者失败的问题

git clone 项目报错 比如使用git clone 下载项目&#xff1a; git clone https://github.com/ChuRuaNh0/FastSam_Awsome_TensorRT.git有时候会报以下错误&#xff1a; fatal: unable to access ‘https://github.com/xxx.git/’: Failed to connect to github.com port 443 …

[读论文]meshGPT

概述 任务&#xff1a;无条件生成mesh &#xff08;无颜色&#xff09;数据集&#xff1a;shapenet v2方法&#xff1a;先trian一个auto encoder&#xff0c;用来获得code book&#xff1b;然后trian一个自回归的transformermesh表达&#xff1a;face序列。face按规定的顺序&a…

大型网站系统架构演化(Web)

大型网站系统架构演化 大型网站系统架构演化需要关注的维度涉及的技术演进过程单体架构垂直架构使用缓存改善网站性能缓存与数据库的数据一致性问题缓存技术对比Redis分布式存储方案Redis集群切片的常见方式Redis数据类型Redis 淘汰算法使用服务集群改善网站并发能力 大型网站系…

Python实现FA萤火虫优化算法优化循环神经网络分类模型(LSTM分类算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 萤火虫算法&#xff08;Fire-fly algorithm&#xff0c;FA&#xff09;由剑桥大学Yang于2009年提出 , …

利用Python中的Manim进行数学绘画和创作

相信很多同学就算没听过3Blue1Brown&#xff0c;也一定曾看过他们出品的视频&#xff0c;其从独特的视觉角度解说各种数学概念&#xff0c;内容包括线性代数、微积分、神经网络、傅里叶变换以及四元数等晦涩难懂的知识点。例如最火的《线性代数本质》系列视频。 那么这些视频是…

[学习记录]Node event loop 总结流程图

文章目录 文章来源根据内容输出的流程图待处理遗留的问题参考 文章来源 详解JavaScript中的Event Loop&#xff08;事件循环&#xff09;机制 根据内容输出的流程图 待处理 这里从polling阶段开始 好像有些问题 遗留的问题 为什么“在I/O事件的回调中&#xff0c;setImmediate…

Android 获取应用签名

Android 获取应用签名 本文主要讲下在android中如何获取应用签名. 也方便平时用来区分一个应用是不是原包应用. 1: 通过PackageManager获取签名信息 首先,通过packageManager获取到指定应用的PackageInfo. 这里需要传入的flag是PackageManager.GET_SIGNATURES /*** {link P…

matlab 汽车单车模型固定点跟踪算法

1、内容简介 略 29-可以交流、咨询、答疑 2、内容说明 单车模型固定点跟踪算法 单车模型&#xff0c;固定点跟踪算法&#xff0c;动画演示&#xff0c; 汽车单车模型、转弯动画、固定点跟踪算法、pid控制 3、仿真分析 略 A[0,5;0,0];B[0;1]; Q10*eye(2);R1; Klqr(A…

2015年五一杯数学建模A题不确定性条件下的最优路径问题解题全过程文档及程序

2015年五一杯数学建模 A题 不确定性条件下的最优路径问题 原题再现 目前&#xff0c;交通拥挤和事故正越来越严重的困扰着城市交通。随着我国交通运输事业的迅速发展&#xff0c;交通“拥塞”已经成为很多城市的“痼疾”。在复杂的交通环境下&#xff0c;如何寻找一条可靠、快…

C++利剑string类(详解)

前言&#xff1a;大家都知道在C语言里面的有 char 类型&#xff0c;我接下来要讲的 string 类功能是使用 char 类型写的类&#xff0c;当然这个是C官方写的&#xff0c;接下来我们将会学会使用它&#xff0c;我们会发现原来 char 这种类型是还能这么好用&#xff0c;授人以…

数学建模-基于LightGBM和BP神经网络的互联网招聘需求分析与预测

基于LightGBM和BP神经网络的互联网招聘需求分析与预测 整体求解过程概述(摘要) 就业是民生之本&#xff0c;是发展之基&#xff0c;也是安国之策。2020 年新冠肺炎疫情的爆发&#xff0c;稳就业成为应对疫情、稳定社会的重要保障之一。随着数据新动能的发展&#xff0c;互联网…

【Linux】Ubuntu添加root用户

在Ubuntu中&#xff0c;默认情况下是禁用了root用户的登录。如果仍然想要启用root用户&#xff0c;并设置root用户的密码&#xff0c;应按照以下步骤进行操作&#xff1a; 一、输入sudo passwd root设置root用户密码 二、切换root用户 sudo -i su root 这两条命令均可却换至…

Tkinter 面向对象框架《一》

一、说明 在本教程中&#xff0c;您将学习如何在 Tkinter 中应用面向对象编程以使代码更有条理。首先介绍Tk下小部件&#xff0c;然后介绍Ttk小部件&#xff0c;即如何从ttk.Frame类继承并在根窗口中使用它。 二、定义 Tkinter 面向对象的窗口 2.1 最基本的对象 以下简单程序创…

数字媒体技术基础之:栅格图像与矢量图形

使用各种软件创建不同类型的复合图像和图稿时&#xff0c;常会遇到基本的数字图像类型&#xff1a;栅格图像和矢量图像。 ◆ ◆ ◆ 栅格图像 栅格图像是基于像素的图像&#xff0c;故又称为“像素图像”或“位图图像”&#xff0c;主要用于编辑照片或制作可在 Web 上轻松使用…

C#语言高阶开发

目录 数据结构 集合 动态数组ArrayList 习题&#xff1a;声明一个Monster类&#xff0c;有一个Attack方法,用一个ArrayList去封装Monster的对象,装10个&#xff0c;遍历monster的list让他们释放攻击方法 哈希表HashTable 创建一个武器类&#xff0c;有一个属性叫做id,每个…

循环队列的结构设计和基本操作的实现(初始化,入队,出队,判空,获取长度,清空,销毁)

目录 1.队列的定义 2.循环队列的设计图示 3.循环队列的结构设计 4.循环队列的实现 5.循环队列的总结 1.队列的定义 和栈相反,队列(queue)是一种先进先出(first in first out,缩写为FIFO)的线性表.它只允许在表的一端进行插入,而在另一端删除元素. 在队列中,允许插入的一…

数据结构与算法之美学习笔记:29 | 堆的应用:如何快速获取到Top 10最热门的搜索关键词?

目录 前言堆的应用一&#xff1a;优先级队列堆的应用二&#xff1a;利用堆求 Top K堆的应用三&#xff1a;利用堆求中位数解答开篇内容小结 前言 本节课程思维导图&#xff1a; 搜索引擎的热门搜索排行榜功能你用过吗&#xff1f;搜索引擎每天会接收大量的用户搜索请求&#x…