当前位置: 首页 > news >正文

C++初阶:内存管理

目录

  • 1. C/C++中各种资源的内存分布
    • 1.1 C/C++程序内存区域划分
    • 1.2 各资源的内存分布情况(练习)
  • 2. C++中的动态内存管理方式
    • 2.1 new/delete开辟内置类型空间
    • 2.2 new/delete开辟销毁自定义类型空间
  • 3. operator new 与 operator delete函数
  • 4. new与delete的实现原理
  • 5. 定位new表达式与池化计数
  • 6. malloc/free与new/delete的异同

1. C/C++中各种资源的内存分布

1.1 C/C++程序内存区域划分

正在执行的程序是在计算机的内存空间上运行的,C/C++为了程序的高效运行,将内存划分了多个区域来进行对不同特性种类资源的区别管理。

在这里插入图片描述

1.2 各资源的内存分布情况(练习)

//全局变量,数据段
int globalVar = 1;//静态全局变量,数据段
static int staticGlobalVar = 1;
void Test()
{//静态变量,数据段static int staticVar = 1;//局部变量,栈int localVar = 1;//局部变量,数组,栈//sizeof(num1),代表整个数组,40字节int num1[10] = { 1, 2, 3, 4 };//局部变量,字符数组,栈//sizeof(char2),代表整个数组,5字节//strlen(char2),字符串长度,4char char2[] = "abcd";//sizeof(pChar3),指针,4/8字节//strlen(pChar3),字符串长度,4//只读字符串,代码段const char* pChar3 = "abcd";//动态开辟空间,堆//ptr1指针,4/8字节int* ptr1 = (int*)malloc(sizeof(int) * 4);//calloc会用0进行申请空间的初始化int* ptr2 = (int*)calloc(4, sizeof(int));int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);free(ptr1);free(ptr3);
}

2. C++中的动态内存管理方式

  1. 在编写程序时,我们时常需要一段可以动态增长我们可以控制其开辟与销毁的空间,C语言中我们学习过在内存中动态开辟空间的方式,我们通过malloc与free来进行空间开辟与销毁。
  2. 而C++中有新的动态开辟空间的方法,new与delete,它们在使用上更加方便,且可以应用于更广泛的场景,接下来,就让我们来进行对其的学习与使用。

2.1 new/delete开辟内置类型空间

new:

//开辟一个指定类型大小的空间
int* ptr1 = new int;//为开辟的空间赋于指值
int* ptr2 = new int(10);//开辟连续n个指定类型大小的空间
int* ptr3 = new int[10];//连续空间的初始化赋值
int* ptr4 = new int[3]{1,2,3};

delete:

//释放大小为1个指定类型大小的空间
delete ptr1;//释放大小为多个指定类型空间大小的空间
delete[] ptr3

注: new/delete 与 new[]/delete[] 必须配合使用,不能混用

2.2 new/delete开辟销毁自定义类型空间

  1. new/delete开辟自定义类型的动态空间,会自动调用自定义类型的构造与析构函数
class A
{
private:int _a;
public:A(int a = 0):_a(a){cout << "A()" << endl;}A(const A& tmp){_a = tmp._a;cout << "A(const A&)" << endl;}	~A(){cout << "~A()" << endl;}
};int main()
{A* pa1 = new A;delete pa1;A* pa2 = new A[3];delete[] pa2;return 0;
}
  1. 开辟自定义类型空间的初始化方式
A aa1;
A aa2;
A aa3;//一段空间
//方法1:(用存在的对象)
A* pa1 = new A(aa1);
//方法2:(创建匿名对象)
A* pa2 = new A(A());
//方法3:(隐式类型转换,构造 + 拷贝构造,优化为构造)
A* pa3 = new A(3);//多段空间
//方法1:
A* pa4 = new A[3]{aa1, aa2, aa3};
//方法2:
A* pa5 = new A[3]{A(), A(), A()};
//方法3:(前三个元素初始化为1,2,3,后面会赋值的部分会全部默认初始化为0,类似数组)
A* pa6 = new A[10]{1, 2, 3};

3. operator new 与 operator delete函数

  1. new和delete是我们进行动态内存申请和释放的操作符,而其实现空间的开辟与释放的方式为,去调用用名为operator new 和operator delete的两个函数。
  2. 这两个函数是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间,接下来就让我们来学习这两个函数的相关知识。(虽然名为operator,但与运算符重载无关,两者为全局函数)
  3. operator new与operator delete的实现,底层为直接调用malloc与free来实现空间的动态开辟,其用法也与malloc/free相同。(operator new与operator delete两者可以直接调用)
int* pa = (int*)operator new(sizeof(int));*pa = 10;
cout << *pa << endl;operator delete(pa);
  1. 既然operator new/delete实现依旧是调用malloc/free实现动态开辟空间,那为什么不去直接使用malloc/free?
  2. malloc申请空间失败会返回0(NULL),此返回值不符合面向对象的编程特性,所以C++对其进行了一层封装,使得申请空间失败后抛出异常,为了与malloc的封装匹配,于是将free也进行了封装,封装为operator delete。
  3. 调用抛出异常演示:
//连续申请空间,申请空间不足,开辟失败
void func_test()
{char* c1 = new char[1024 * 1024 * 1024];//cout << c1 << endl;//char类型的变量,流插入操作自动识别时会默认识别为字符串而不是指针cout << (void*)c1 << endl;//捕获异常的操作会改变执行流,不再执行下面操作,而会抛出异常//类似gotochar* c2 = new char[1024 * 1024 * 1024];cout << (void*)c2 << endl;
}int main()
{try{func_test();}catch (const exception& e){cout << e.what() << endl;}return 0;
}

4. new与delete的实现原理

  1. 申请开辟自定义类型空间时,new操作符会先开辟出指定大小的空间,而后调用构造函数初始化开辟出的空间。(申请空间 + 调用构造)

在这里插入图片描述

  1. delelte销毁自定义类型的申请空间时,会先调用自定义类型的析构函数,而后再进行空间的销毁释放。(调用析构,销毁空间)

在这里插入图片描述

  1. new/delete操作符在编译时会直接按照上述调用步骤,生成汇编指令。(汇编调试,call,jump)
  1. new调用new,申请时,会先开辟空间,再调用构造,构造时再调用new。销毁时,会先调用析构,析构先销毁里层new申请的空间,而后再销毁外层new申请的空间。

在这里插入图片描述

class Stack
{
private:int* _a;int _capacity;int _top;
public:Stack(int n = 4){cout << "Stack()" << endl;_a = new int[n];_top = 0;_capacity = 4;}~Stack(){cout << "~Stack()" << endl;delete[] _a;_top = 0;_capacity = 0;}
};int main()
{Stack* p1 = new Stack;delete[] p1;return 0;
}
  1. new/delete与new[]/delete[]不匹配使用可能会产生的风险与delete[]的工作原理:
    (汇编调试)
int main()
{Stack* p1 = new Stack[10];delete[] p1;return 0;
}

在这里插入图片描述

  1. 10个Stack类型的变量组成的空间大小应为120字节,可是,经过汇编调试后,大小却为124字节。这是因为,new[]申请连续的自定义类型空间时,会额外在头部申请一个四字节大小的空间用来存放申请的自定义类型空间个数,delete[]在析构时会向前调整四个字节,读取需要调用析构的次数。(delete获取需要调用析构的次数)

在这里插入图片描述

  1. 当使用delete去释放new[] 出的空间时,不会向前调整四个字节,而是调用一次析构函数后,从第一个元素的首地址释放空间,会导致内存泄漏。
  1. 编译器的优化:因为类A的成员变量只有一个int变量,且构造时没有开辟额外空间,当我们将类A的析构函数屏蔽后(没有使用析构的必要),当再次new[]一段连续空间时,将不再于头部开辟额外空间存储元素个数。delete所要做的就只是释放开辟的空间,这一点,使用free也同样可以做到。
class A
{
private:int _a;
public:A(){cout << "A()" << endl;}/*~A(){cout << "~A()" << endl;}*/
};int main()
{A* pa = new A[10];//用delete去销毁new[]申请的空间delete pa;//free(pa);return 0;
}

5. 定位new表达式与池化计数

  1. 在前面的学习中我们了解到,类的析构函数可以显示调用,而构造函数却不可以。那么,当我们不使用new的方式开辟出自定义类型的空间后,有没有办法对这段空间使用构造函数进行初始化呢,接下来,我们引入定位new表达式。
//调用默认构造
new(指针:指定空间地址)类型
//赋予初始值
new(指针)类型(初始值)

示例:

class A
{
private:int _a;
public:A(int a = 0):_a(a){cout << "A()" << endl;}~A(){cout << "~A()" << endl;}
};int main()
{A* pa1 = (A*)operator new(sizeof(A));//调用构造new(pa1)A;//析构pa1->~A();//释放空间operator delete(pa1);A* pa2 = (A*)operator new(sizeof(A));//调用构造,并指定初始值new(pa1)A(10);pa2->~A();operator delete(pa2);return 0;
}
  1. 定位new应用场景:
    <1> 因为new申请动态开辟的空间是在堆上,很多时候我们需要频繁的调用申请空间,这样的效率非常低。
    <2> 所以,C++中创建了内存池来应对这一类问题,先提前申请出一大块空间备用,这块空间被称为内存池,当我们需要申请空间时,不用再去堆上申请,而是可以直接找内存池申请划分。
    <3> 这些申请来的空间(自定义类型)是已经开辟好的,这些空间没有调用构造函数进行初始化,无法使用,而定位new就可以解决这样的问题。

6. malloc/free与new/delete的异同

相同点:

  1. 都是于堆区上开辟空间,且都需要手动释放

不同点:

  1. malloc申请的空间不会进行初始化,而new申请出的空间可以进行初始化
  2. malloc申请空间需要计算空间大小,而new只需要指定对象个数
  3. malloc的返回必须要强转为对应类型的指针,而new在申请空间时就声明了类型
  4. malloc申请空间失败返回空,new申请失败抛出异常
  5. malloc开辟销毁空间时不会调用构造,析构函数,而new/delete开辟销毁空间时会调用构造与析构

相关文章:

C++初阶:内存管理

目录 1. C/C中各种资源的内存分布1.1 C/C程序内存区域划分1.2 各资源的内存分布情况&#xff08;练习&#xff09; 2. C中的动态内存管理方式2.1 new/delete开辟内置类型空间2.2 new/delete开辟销毁自定义类型空间 3. operator new 与 operator delete函数4. new与delete的实现…...

vue和react的diff算法源码

Vue.js 中的虚拟 DOM Diff 算法是其性能优化的关键之一。 Vue.js 的 Diff 算法主要基于 Snabbdom&#xff0c;以下是 Vue.js 中虚拟 DOM Diff 算法的简化版伪代码&#xff0c;以便说明其基本思想&#xff1a; function patch(oldVnode, vnode) {// 如果 oldVnode 不存在&…...

Coordinate Attention(CVPR 2021)

paper&#xff1a;Coordinate Attention for Efficient Mobile Network Design official implementation&#xff1a;GitHub - houqb/CoordAttention: Code for our CVPR2021 paper coordinate attention 背景 注意力机制&#xff0c;已经被广泛用于提高深度神经网络的性能&…...

计算机网络-第4章 网络层(2)

主要内容&#xff1a;网络层提供的两种服务&#xff1a;虚电路和数据报&#xff08;前者不用&#xff09;、ip协议、网际控制报文协议ICMP、路由选择协议&#xff08;内部网关和外部网关&#xff09;、IPv6,IP多播&#xff0c;虚拟专用网、网络地址转换NAT&#xff0c;多协议标…...

重学SpringBoot3-WebMvcAutoConfiguration类

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-WebMvcAutoConfiguration类 是什么什么用生效条件作用 自定义配置的三种方式自定义配置举例1. 自定义 DispatcherServlet 配置2. 静态资源配置3. 自定义…...

探索数据结构:深入了解顺序表的奥秘

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;数据结构与算法 贝蒂的主页&#xff1a;Betty’s blog 1. 什么是顺序表 顺序表是用一段物理地址连续的存储单元依次存储数据元…...

苍穹外卖学习-----2024/03/010---redis,店铺营业状态设置

1.Redis入门 2.在Java中操作Redis 3.店铺营业状态设置 BUG!!! 今天在启动项目时&#xff0c;用到了Redis缓存数据库&#xff0c;但是却出现了报错信息&#xff1a; ERR Client sent AUTH, but no password is set。Caused by: io.lettuce.core.RedisCommandExecutionException…...

RUST 每日一省:发布到crates.io

github是开源代码分享的地方&#xff0c;rust的开源项目除了github&#xff0c;我们还可以将其发布到 crates.io 上&#xff0c;然后其它用户就可以使用cargo进行安装使用了。其实步骤很简单&#xff0c;只有三条命令了&#xff0c;我们一次来看一下。 1、cargo package 首先&a…...

String类及其常用方法

文章目录 1.String类的特性与使用1.1 String类的特性1.2 String对象的创建方式1.3 String 的使用&#xff08;不同的拼接操作&#xff09; 2.String常用方法2.1 String的常用方法一2.2 String常用方法二2.3 String常用方法三 1.String类的特性与使用 1.1 String类的特性 Stri…...

1094. 拼车

说在前面 &#x1f388;不知道大家对于算法的学习是一个怎样的心态呢&#xff1f;为了面试还是因为兴趣&#xff1f;不管是出于什么原因&#xff0c;算法学习需要持续保持。 题目描述 车上最初有 capacity 个空座位。车 只能 向一个方向行驶&#xff08;也就是说&#xff0c;不…...

Docker进阶:深入了解容器数据卷

Docker进阶&#xff1a;深入了解容器数据卷 一、前言二、容器数据卷的作用三、容器数据卷的使用方法四、实战--使用docker部署前端项目&#xff08;数据卷挂载&#xff09;4.1 重要&#xff1a;准备工作&#xff0c;先在本地创建挂载目录4.2 启动一个临时的nginx容器&#xff0…...

升级版本彻底解决bootstrap-table-fixed-columns固定列后行对不齐问题

升级到bootstrap-table和bootstrap-table-fixed-columns版本都升级到v1.22.3版本以上&#xff0c;即可解决该问题 bootstrap-table&#xff1a;bootstrap-table/dist/bootstrap-table.min.css at develop wenzhixin/bootstrap-table GitHub bootstrap-table-fixed-columns&…...

打破边界:深入探索STUN在实现无缝NAT穿越和WebRTC通信中的核心作用

引言 STUN是一个网络协议&#xff0c;设计用于帮助在网络地址转换&#xff08;NAT&#xff09;后面的设备发现其公网地址和端口号。通过允许这些设备发现自己从外部看到的地址&#xff0c;STUN使得它们能够在NAT或防火墙背后建立端到端的通信&#xff0c;这对于VoIP、视频会议…...

浅谈 前端的动态绑定属性

目录 前言1. 基本知识2. Demo 前言 作为Java开发者&#xff0c;从开发转到全栈&#xff0c;前端好些细节都需要科普&#xff0c;这不就来个动态绑定属性 起因是这个&#xff1a; <uni-tr> <uni-td align"center" :rowspan"checkTypesCount 1"…...

Sklearn支持向量机

支持向量机&#xff08;Support Vector Machine, SVM&#xff09;是一种常用的分类算法&#xff0c;它可以用于解决二分类和多分类问题。在Python中&#xff0c;你可以使用Sklearn库来实现SVM。下面是一个简单的例子&#xff0c;展示了如何使用Sklearn进行SVM分类。 # 导入必要…...

【Lazy ORM】 小工具 acw 本地客户端 你负责点击页面,他负责输出代码

介绍 wu-smart-acw-client 简称acw-client&#xff0c;是一个基于Lazy ORM定制的客户端代码生成小工具 Lazy ORM 小工具 acw 本地客户端 你负责点击页面&#xff0c;他负责输出代码安装 <dependency><groupId>top.wu2020</groupId><artifactId>wu-sma…...

《详解:鸿蒙NEXT开发核心技术》

我们现在都知道鸿蒙作为一个国产的全栈自研系统&#xff0c;经过国家主推后。已经引起人们很大的关注&#xff0c;其中作为开发者来说&#xff1b;许多一线大厂已经与其华为鸿蒙展开原生应用的合作了&#xff0c;目前了解到已经有200家。而之后出现了很多的高薪鸿蒙开发岗位&am…...

快速排序 刷题笔记

思路 分治双指针 在每个区间选定一个基准目标 两个指针从数组的两边向中间推进 使用 while循环判断 do {i;}while(q[i]<x); do{j--;}while(q[j]>x); 每次这样做完就会找到q[i]>x,,,,q[j]小于x 此时我们交换 q[i] ,q[j]于是小于x的数分到了小于x的一侧 大…...

DAY by DAY 史上最全的Linux常用命令汇总----man

man是按照手册的章节号的顺序进行搜索的。 man设置了如下的功能键&#xff1a; 功能键 功能 空格键 显示手册页的下一屏 Enter键 一次滚动手册页的一行 b 回滚一屏 f 前滚一屏 q 退出man命令 h 列出所有功能键 /word 搜索word字符串 注意&#xff1a…...

十六、接口隔离原则、反射、依赖注入

接口隔离原则、反射、特性、依赖注入 接口隔离原则 客户端不应该依赖它不需要的接口&#xff1b;一个类对另一个类的依赖应该建立在最小的接口上。 五种原则当中的i 上一章中的接口&#xff0c;即契约。 契约就是在说两件事&#xff0c;甲方说自己不会多要&#xff0c;乙方会在…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

Linux简单的操作

ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

uniapp中使用aixos 报错

问题&#xff1a; 在uniapp中使用aixos&#xff0c;运行后报如下错误&#xff1a; AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

Element Plus 表单(el-form)中关于正整数输入的校验规则

目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入&#xff08;联动&#xff09;2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...

JVM 内存结构 详解

内存结构 运行时数据区&#xff1a; Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器&#xff1a; ​ 线程私有&#xff0c;程序控制流的指示器&#xff0c;分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 ​ 每个线程都有一个程序计数…...

音视频——I2S 协议详解

I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议&#xff0c;专门用于在数字音频设备之间传输数字音频数据。它由飞利浦&#xff08;Philips&#xff09;公司开发&#xff0c;以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...

规则与人性的天平——由高考迟到事件引发的思考

当那位身着校服的考生在考场关闭1分钟后狂奔而至&#xff0c;他涨红的脸上写满绝望。铁门内秒针划过的弧度&#xff0c;成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定"&#xff0c;构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...

React从基础入门到高级实战:React 实战项目 - 项目五:微前端与模块化架构

React 实战项目&#xff1a;微前端与模块化架构 欢迎来到 React 开发教程专栏 的第 30 篇&#xff01;在前 29 篇文章中&#xff0c;我们从 React 的基础概念逐步深入到高级技巧&#xff0c;涵盖了组件设计、状态管理、路由配置、性能优化和企业级应用等核心内容。这一次&…...