【CPP】CPP的内存管理

目录

      • 10 C/C++内存管理
        • 10.1 内存分布
        • 10.2 C的动态内存管理
        • 10.3 C++的内存管理
        • 10.4 `new`失败的检测
        • 10.5 `operator new`与`operator delete`函数
        • 10.5 `new`与`malloc()`的区别,`delete`与`free()`的区别
        • 10.6 定位`new`表达式

这里是oldking呐呐,感谢阅读口牙!先赞后看,养成习惯!
个人主页:oldking呐呐
专栏主页:深入CPP语法口牙

10 C/C++内存管理

10.1 内存分布
  • CPP的内存划分和C保持一致
    • 栈 – 用于非静态局部变量/函数参数/返回值,栈是向下增长的
    • 内存映射段 – 高效的I/O映射方式,用于装载一个共享的动态内存库,用户可以使用系统接口创建共享内存,做进程间通信(暂时简单了解即可)
    • 堆 – 用于程序运行时的动态内存分配,堆可以向上增长
    • 数据段 – 存储全局数据和静态数据
    • 代码段 – 可执行的代码/只读常量
10.2 C的动态内存管理
  • malloc(),calloc(),realloc()用于申请空间(调整空间)
  • free()用于释放空间
10.3 C++的内存管理
  • CPP可以继续使用C的内存管理方式,但CPP引入了更加方便的关键字用于内存管理,即newdelete

  • 基础玩法:

int main()
{int* pa1 = new int; //内置类型声明+定义int* pa2 = new int(1); //内置类型声明+定义+初始化delete pa1; //内置类型销毁delete pa2; //内置类型销毁int* pb1 = new int[10]; //数组声明+定义int* pb2 = new int[10] {0}; //数组声明+定义+初始化(全为0)int* pb3 = new int[10] {1, 2, 3, 4, 5, 6}; //数组声明+定义+初始化(前六个分别为1,2,3,4,5,6,后4个默认全是0)delete[] pb1; //数组销毁delete[] pb2; //数组销毁delete[] pb3; //数组销毁return 0;
}
  • 进阶玩法(完全区别于C语言的玩法):
class A
{
public:A(int x = 1):_x(x){cout << "A(int x = 1)->" << _x << endl;}A(const A& a):_x(a._x){cout << "A(const A& a)->" << _x << endl;}~A(){_x = 0;cout << "~A()" << endl;}private:int _x = 1;
};int main()
{A* ca1 = new A; //自定义对象的声明+定义+使用缺省值初始化A* ca2 = new A(3); //自定义对象的声明+定义+使用用户的手动传值初始化delete ca1; //自定义类型的销毁delete ca2; //自定义类型的销毁cout << endl; //分割cout << "-------------------------" << endl; //分割cout << endl; //分割A* ca3 = new A[3]; //自定义类型数组的声明+定义+使用缺省值初始化A* ca4 = new A[3]{ 0 }; //自定义类型数组的声明+定义+第一个数使用用户传值初始化,其他数使用缺省值进行初始化A* ca5 = new A[3]{ 5, 6 }; //自定义类型数组的声明+定义+第一/二个数使用用户传值初始化,其他数使用缺省值初始化delete[] ca3; //自定义类型数组的销毁delete[] ca4; //自定义类型数组的销毁delete[] ca5; //自定义类型数组的销毁return 0;
}
  • 区别于传统C的写法,newdelete最大的优势在于它会自动调用构造函数和析构函数

请添加图片描述

  • 如果是多参数的话:
class A
{
public:A(int x = 1, int y = 2):_x(x),_y(y){cout << "A(int x = 1)->" << _x << " " << _y << endl;}A(const A& a):_x(a._x),_y(a._y){cout << "A(const A& a)->" << _x << " " << _y << endl;}~A(){_x = 0;cout << "~A()" << endl;}private:int _x = 1;int _y = 2;
};int main()
{A* ca1 = new A; //用缺省值初始化A* ca2 = new A(3, 4); //用用户传值初始化A* ca3 = new A[2]{ A(5, 6), A(7, 8)}; //用临时变量初始化A* ca4 = new A[2]{ {10, 11},{12, 13} }; //用隐式类型转换初始化delete ca1; //销毁delete ca2;delete[] ca3;delete[] ca4;return 0;
}

请添加图片描述

  • 不难看出,这里是有进行优化的,编译器把临时对象省略掉了(VS2022-debug-x64)
10.4 new失败的检测
  • 这一部分的内容暂时知道这么回事就可以了,涵盖了异常处理的一些内容
int main()
{try //尝试{void* ca2 = new char[1024 * 1024 * 1024];cout << ca2 << endl;void* ca3 = new char[1024 * 1024 * 1024];cout << ca3 << endl;}catch (const exception& e) //尝试失败就会调用catch一个叫exception的东西,exception是一个异常类型{cout << e.what() << endl; //打印发生了什么}return 0;
}

请添加图片描述

  • 在32位下,程序所开辟的空间不够,所以报出开空间错失败的报错信息

  • 不仅仅new可以抛异常,函数也可以抛异常

void func1()
{void* ca2 = new char[1024 * 1024 * 1024];cout << ca2 << endl;void* ca3 = new char[1024 * 1024 * 1024];cout << ca3 << endl;
}int main()
{try{func1();}catch (const exception& e){cout << e.what() << endl;}return 0;
}

请添加图片描述

  • new一般不会失败,不过还是要检测的
10.5 operator newoperator delete函数
  • operator newoperator delete是系统写的两个全局函数

  • 简单来说,为了让new能套用进C++异常抛出的那套逻辑里,设计者把抛出逻辑直接写进operator new里,我们可以直接看以下底层代码

void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{void *p;while ((p = malloc(size)) == 0) //底层其实还是mallocif (_callnewh(size) == 0){static const std::bad_alloc nomem; //如果内存申请失败,就会进到这个if里,并抛出错误异常_RAISE(nomem);}return (p);
}
  • 简单来说operator new = malloc() + 判断与抛出错误异常

  • operator delete也是一样

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;
}
  • 我们能看到,在operator delete里,使用到了一个叫做_free_dbg的东西,其实这个东西就是free()的宏定义
	#define free(p) _free_dbg(p, _NORMAL_BLOCK)
  • 事实上,operator newoperator delete其实也并不是new或者delete的定义,前者只是后者的一部分而已

  • 现在我们整一个自定义类型,用new申请空间到堆上,此时打开反汇编

006B2704  push        8  
006B2706  call        operator new (06B1140h)  
006B270B  add         esp,4  
006B270E  mov         dword ptr [ebp-0ECh],eax  
006B2714  mov         dword ptr [ebp-4],0  
006B271B  cmp         dword ptr [ebp-0ECh],0  
006B2722  je          __$EncStackInitStart+61h (06B273Bh)  
006B2724  push        2  
006B2726  push        1  
006B2728  mov         ecx,dword ptr [ebp-0ECh]  
006B272E  call        A::A (06B1366h)  
006B2733  mov         dword ptr [ebp-0F4h],eax  
006B2739  jmp         __$EncStackInitStart+6Bh (06B2745h)  
006B273B  mov         dword ptr [ebp-0F4h],0  
006B2745  mov         eax,dword ptr [ebp-0F4h]  
006B274B  mov         dword ptr [ebp-0E0h],eax  
006B2751  mov         dword ptr [ebp-4],0FFFFFFFFh  
006B2758  mov         ecx,dword ptr [ebp-0E0h]  
006B275E  mov         dword ptr [a],ecx  
  • 不难发现,这条语句一定做了两件事情

    1. 006B2706 call operator new (06B1140h) 这条语句调用了operator new
    2. 006B272E call A::A (06B1366h) 这条语句调用了构造函数
  • 所以我们可以理解为,设计者为了让malloc()能套用进CPP的体系中,将malloc()升级加强成为了new

  • 一个给自定义类型开辟空间的new一定干了3件事

    • malloc()开了空间
    • 检测异常,如果有就抛出异常
    • 调用自定义对象的构造函数
  • 现在再来看一看delete

00AB6721  mov         eax,dword ptr [a]  
00AB6724  mov         dword ptr [ebp-0F8h],eax  
00AB672A  cmp         dword ptr [ebp-0F8h],0  
00AB6731  je          __$EncStackInitStart+0AEh (0AB6748h)  
00AB6733  push        1  
00AB6735  mov         ecx,dword ptr [ebp-0F8h]  
00AB673B  call        A::`scalar deleting destructor' (0AB14ECh)  
00AB6740  mov         dword ptr [ebp-100h],eax  
00AB6746  jmp         __$EncStackInitStart+0B8h (0AB6752h)  
00AB6748  mov         dword ptr [ebp-100h],0 
  • 接着转到00AB673B call A::`scalar deleting destructor'
00AB2410  push        ebp  
00AB2411  mov         ebp,esp  
00AB2413  sub         esp,0CCh  
00AB2419  push        ebx  
00AB241A  push        esi  
00AB241B  push        edi  
00AB241C  push        ecx  
00AB241D  lea         edi,[ebp-0Ch]  
00AB2420  mov         ecx,3  
00AB2425  mov         eax,0CCCCCCCCh  
00AB242A  rep stos    dword ptr es:[edi]  
00AB242C  pop         ecx  
00AB242D  mov         dword ptr [this],ecx  
00AB2430  mov         ecx,dword ptr [this]  
00AB2433  call        A::~A (0AB14F1h)  
00AB2438  mov         eax,dword ptr [ebp+8]  
00AB243B  and         eax,1  
00AB243E  je          __$EncStackInitStart+31h (0AB244Eh)  
00AB2440  push        8  
00AB2442  mov         eax,dword ptr [this]  
00AB2445  push        eax  
00AB2446  call        operator delete (0AB109Bh)  
00AB244B  add         esp,8  
00AB244E  mov         eax,dword ptr [this]  
00AB2451  pop         edi  
00AB2452  pop         esi  
00AB2453  pop         ebx  
00AB2454  add         esp,0CCh  
00AB245A  cmp         ebp,esp  
00AB245C  call        __RTC_CheckEsp (0AB12FDh)  
00AB2461  mov         esp,ebp  
00AB2463  pop         ebp  
00AB2464  ret         4  
00AB2467  int         3  
00AB2468  int         3  
00AB2469  int         3  
00AB246A  int         3  
00AB246B  int         3  
00AB246C  int         3  
00AB246D  int         3  
00AB246E  int         3  
00AB246F  int         3  
  • 不难看出,这里有两个语句

    • 00AB2433 call A::~A (0AB14F1h) – 调用析构
    • 00AB2446 call operator delete (0AB109Bh) – 调用operator delete
  • 其实也就是说delete就是free()的升级版

    1. 调用析构
    2. 调用free()
  • new[]delete[]的底层调用了叫operator new[]operator delete[],但operator new[]operator delete[]的底层还是newdelete这俩,其实就是套娃

10.5 newmalloc()的区别,deletefree()的区别
  • newmalloc()的区别,主要还是体现在CPP引入的一些新内容上,为了适配CPP引入的新内容,前者是后者的升级版

    1. new不需要手动设定开辟空间的大小,而malloc()需要手动设定空间开辟的大小
    2. new能完成异常抛出,而malloc()不行,malloc()需要手动判空
    3. new能调用构造函数进行初始化,但malloc()不行
    4. malloc()返回的值为void*,需要强转成需要的类型,而new不需要强转,他自动返回相应类型的指针
    5. new是操作符,malloc()是函数
  • 同理,delete也相当于是free()的升级版

    1. delete会调用析构函数释放资源,但free()就只是负责把空间归还
    2. delete是操作符,free()是函数
10.6 定位new表达式
  • 先看一个例子
class A
{
public:A(int x = 1, int y = 2)//默认构造:_x(x),_y(y){cout << "A(int x = 1, int y = 2)->" << _x << " " << _y << endl;}A(const A& ra)//拷贝构造:_x(ra._x),_y(ra._y){cout << "A(const A& ra)->" << _x << " " << _y << endl;}~A()//析构{cout << "~A()" << endl;}private:int _x;int _y;
};int main()
{A* a1 = new A(1, 2); //声明+定义+初始化a1delete a1; //调用析构+释放内存(上面和下面完全没有区别,下面的情况完全就是脱裤子放屁,多此一举)//------------------------------------------------A* a2 = (A*)operator new(sizeof(A)); //声明+定义a1new(a2)A(1, 2); //初始化a1(其实这里就是在调用构造函数)a2->~A(); //调用析构函数operator delete(a2); //释放内存return 0;
}
  • 可以看到,operator newoperator delete这俩函数都可以手动调用,包括~A(),也是可以手动调用的,但构造函数没办法手动调用,就是能靠new(a2)A(1, 2)这个语句来调用,new后面括号里放对象名,紧接着对象类型,在后面括号里放传参的内容

  • 大部分情况下,正常使用newdelete就能完成任务了,少部分情况例如在内存池中就必须使用operator newoperator delete

内存池是向堆申请的一部分空间,用来专门存放经常需要申请和释放内存的数据,内存池与堆是独立开的,所以如果想从内存池中申请空间,就不能用在堆申请空间的那套模式,于是就有了以上模式,相当于手动模拟申请空间,只不过实际上申请的是内存池的空间

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

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

相关文章

编程中的智慧四:设计模式总览

前面三篇我们通过从一些零散的例子&#xff0c;和简单应用来模糊的感受了下设计模式在编程中的智慧&#xff0c;从现在开始正式进入设计模式介绍&#xff0c;本篇将从设计模式的7大原则、设计模式的三大类型、与23种设计模式的进行总结&#xff0c;和描述具体意义。 设计模式体…

使用Diffusion Models进行街景视频生成

Diffusion Models专栏文章汇总&#xff1a;入门与实战 前言&#xff1a;街景图生成相当有挑战性&#xff0c;目前的文本到视频的方法仅限于生成有限范围的场景的短视频&#xff0c;文本到3D的方法可以生成单独的对象但不是整个城市。除此之外街景图对一致性的要求相当高&#x…

数据库基础与安装MYSQL数据库

一、数据库管理系统DBMS 数据库技术是计算机科学的核心技术之一&#xff0c;具有完备的理论基础。使用数据库可以高效且条理分明地存储数据&#xff0c;使人们能够更加迅速、方便地管理数据 1.可以结构化存储大量的数据信息&#xff0c;方便用户进行有效的检索和访问 2.可以…

目前航空航天设备怎么减重设计

目前航空航天设备怎么减重设计 1.使用轻质高强度材料1.1复合材料1.2金属基复合材料1.3陶瓷基复合材料1.4功能梯度材料和蜂窝材料 2.结构优化设计2.1拓扑优化2.2仿生学设计 3.部件和系统轻量化3.1机载娱乐系统3.2航空线缆3.3激光焊接技术 4.发动机和推进系统的优化4.1轻量化发动…

python爬虫Selenium模块及测试案例详解

什么是selenium&#xff1f; &#xff08;1&#xff09;Selenium是一个用于Web应用程序测试的工具。 &#xff08;2&#xff09;Selenium 测试直接运行在浏览器中&#xff0c;就像真正的用户在操作一样。 &#xff08;3&#xff09;支持通过各种driver&#xff08;FirfoxDrive…

python gradio 的输出展示组件

HTML&#xff1a;展示HTML内容&#xff0c;适用于富文本或网页布局。JSON&#xff1a;以JSON格式展示数据&#xff0c;便于查看结构化数据。KeyValues&#xff1a;以键值对形式展示数据。Label&#xff1a;展示文本标签&#xff0c;适用于简单的文本输出。Markdown&#xff1a;…

JavaScript之WebAPIs-BOM

目录 BOM操作浏览器一、Window对象1.1 BOM&#xff08;浏览器对象模型&#xff09;1.2 定时器-延时函数1.3 js执行机制1.4 location对象1.5 navigator对象1.6 history对象 二、本地存储三、补充数组中的map方法数组中的join方法数组中的forEach方法(重点)数组中的filter方法(重…

Linux——Centos系统安装(动图演示)

一、创建虚拟机并做相应配置 打开VMware Workstation&#xff0c;选择创建新的虚拟机&#xff1b; 1、选择自定义选项&#xff1a;点击下一步 2、选择虚拟机硬件兼容性&#xff1a;直接下一步就行了&#xff1b;点击下一步 3、安装客户机操作系统&#xff1a;这里我们选择稍后安…

C++对象模型之绕过private权限修饰符

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、C对象模型二、演示1.类层次2.内存排列 总结 前言 咱们都知道C语言在创建类的时候data member&#xff08;数据成员&#xff09;和fuchtion member&#xf…

Linux操作系统的有关常用的命令

1.linux系统的概述 1.1 什么是Linux系统? Linux&#xff0c;全称GNU/Linux&#xff0c;是一种免费使用和自由传播的类UNIX操作系统&#xff0c;其内核由林纳斯本纳第克特托瓦 兹&#xff08;Linus Benedict Torvalds&#xff09;于1991年10月5日首次发布&#xff0c;它主要受…

LVGL项目实战之UI规划

LVGL项目实战之UI规划 ** 实物购买&#xff1a;TB 南山府嵌入式 ** 我们在在做项目之前&#xff0c;先需要确定项目的需求以及可能实现的功能&#xff0c;我们只有确定这些才能够对整体的框架进行把握。 本小结就说一下我们这个项目的一个整体的框架结构以及功能。 1-硬件构…

C语言实现二叉树以及二叉树的详细介绍

目录 1.树概念及结构 1.1树的概念 1.2树的相关概念 1.3树的表示 2.二叉树概念及结构 2.1二叉树的概念 2.2特殊的二叉树 2.3二叉树的性质 2.4二叉树的存储结构 3.二叉树顺序结构--特殊的二叉树--堆及其实现 3.1堆的概念及结构 3.2堆的实现 3.2.1堆的结构 3.2.2堆…

《JavaSE》---21.<简单认识Java的集合框架包装类泛型>

目录 前言 一、什么是集合框架 1.1类和接口总览 二、集合框架的重要性 2.1 开发中的使用 2.2 笔试及面试题 三、背后所涉及的数据结构 3.1 什么是数据结构 3.2 容器背后对应的数据结构 四、包装类 4.1 基本数据类型和对应的包装类 4.2 装箱和拆箱 1.最初的写法 2.…

org.springframework.context.ApplicationContext发送消息

1、创建消息的实体类 package com.demo;/*** 监听的实体类**/ public class EventMessage {private String name;public EventMessage(String name) {this.name name;}public String getName() {return name;}public void setName(String name) {this.name name;} }2、创建消…

【Linux】如何使用docker快速部署Stirling-PDF并实现远程处理本地文档

文章目录 前言1. 安装Docker2. 本地安装部署StirlingPDF3. Stirling-PDF功能介绍4. 安装cpolar内网穿透5. 固定Stirling-PDF公网地址 前言 本篇文章我们将在Linux上使用Docker在本地部署一个开源的PDF工具——Stirling PDF&#xff0c;并且结合cpolar的内网穿透实现公网随时随…

Java 集合框架:Java 中的双端队列 ArrayDeque 的实现

大家好&#xff0c;我是栗筝i&#xff0c;这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 019 篇文章&#xff0c;在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验&#xff0c;并希望进…

共享模型之无锁

一、问题提出 1.1 需求描述 有如下的需求&#xff0c;需要保证 account.withdraw() 取款方法的线程安全&#xff0c;代码如下&#xff1a; interface Account {// 获取余额Integer getBalance();// 取款void withdraw(Integer amount);/*** 方法内会启动 1000 个线程&#xf…

GraphPad prism处理cck-8获得ic50

C组为空白对照组&#xff0c;a组为dmso对照组&#xff0c;b组为细胞加药组&#xff0c;八个梯度的药物浓度 一、数据转化 首先&#xff0c;打开软件&#xff0c;选项中选择x的第一项&#xff0c;y的第二项&#xff0c;单一药物浓度设定了几个孔就选几 把自己的药物浓度直接复制…

ubuntu22安装拼音输入法

专栏总目录 一、安装命令&#xff1a; sudo apt update sudo apt install fcitx sudo apt install fcitx-pinyin 二、切换输入法

游戏常用运行库安装包 Game Runtime Libraries Package

游戏常用运行库安装包&#xff08;Game Runtime Libraries Package&#xff09;是一个整合了多种游戏所需运行库的安装程序&#xff0c;旨在帮助玩家和开发者解决游戏无法正常运行的问题。该安装包支持从Windows XP到Windows 11的系统&#xff0c;并且具备自动检测系统并推荐合…