C++中的内存管理

✨前言✨

📘 博客主页:to Keep博客主页
🙆欢迎关注,👍点赞,📝留言评论
⏳首发时间:2023年11月21日
📨 博主码云地址:博主码云地址
📕参考书籍:《C++ Primer》《C++编程规范》
📢编程练习:牛客网+力扣网
由于博主目前也是处于一个学习的状态,如有讲的不对的地方,请一定联系我予以改正!!!

C++内存管理

  • 1 C++内存分布
  • 2 C++内存管理方式
    • 2.1 处理内置类型
    • 2.2 处理自定义类型
  • 3 operator new与operator delete函数
  • 4 定位new表达式
  • 5 malloc/free和new/delete的区别

1 C++内存分布

在这里插入图片描述
C++根据不同的数据需求有着不同的存储特性,所以将内存分为多个区域来存储数据!

2 C++内存管理方式

在C语言中,我们是利用malloc/calloc/relloc进行动态内存管理!其中relloc就是对之前malloc申请的空间进行扩展,calloc就是malloc加上memset(),而C++是通过new和delete操作符进行动态内存管理

2.1 处理内置类型

利用new和delete操作符进行动态内存管理比C语言中的malloc方便好写了许多!

int main()
{int* b1 = new int;    //申请一个int的空间int* b2 = new int[5]; //利用new[]操作符,申请5个连续int的空间,就是一个数组delete b1;delete[] b2;return 0;
}

通过调试,我们可以进一步的了解new和delete操作符是如何处理内置类型的!
在这里插入图片描述
可以发现,new其实和C语言中的malloc是一样的,也是不会进行初始化的,只是动态开辟空间!相比较于malloc而言,new是不用进行强制类型转换,也不用自己去计算要开辟空间的大小了!同理,delete就相当于free,释放对应开辟的空间!

注意:new和delete要搭配使用,new[]和delete[]搭配使用!不要混着使用,否则会出现内存泄露或者程序不能正常运行的问题!

利用new其实也是可以对内置类型进行初始化的!代码如下:

int main()
{int* p1 = new int(10); //开辟一个int空间,结合()中的内容进行初始化int* p2 = new int[5] {1, 2, 3}; //开辟5个连续的int空间,结合{}中的内容进行初始化delete p1;delete[] p2;return 0;
}

调试结果如图所示:
在这里插入图片描述
可以发现对于5个连续的int空间,只要我们给定一些空间的初始化,后面的空间编译器就把它初始化成为0的!

2.2 处理自定义类型

对于自定义类型如果使用malloc,我们就无法对自定义类型进行初始化!因为构造函数不可以通过所给的对象指针直接调用:
在这里插入图片描述
利用new和delete操作符如何处理自定义类型呢?我们可以参考如下代码进行理解:

class Date {
private:int _year;int _month;int _day;public:Date(int year = 2023 , int month = 11, int day = 21){cout << "Date 构造函数调用了" << endl;_year = year;_month = month;_day = day;}~Date(){cout << "析构函数被调用了" << endl;}
};int main()
{Date* d1 = new Date(2023,11,21);Date* d2 = new Date[5]{ Date(2023,11,20) };//初始化一部分,其他的是默认构造初始化,如果没有默认构造也会报错Date* d3 = new Date;//调用默认构造,无默认构造就会报错delete d1;delete[] d2;delete d3;return 0;
}

运行结果如下:
在这里插入图片描述

从以上我们就可以知道new和delete处理自定义类型的原理

new就是开辟对象空间+调用构造函数,malloc就不会调用构造函数
delete就是调用析构函数+释放对象空间,free就不会调用析构函数

3 operator new与operator delete函数

operator new和operator delete是系统提供的全局函数!我们先来看这样一段代码:

class Stack {
private:int* p;int _capacity;int _top;public:Stack(int capacity = 4){cout << "Stack(int capacity = 4)" << endl;p = new int[capacity];_capacity = capacity;_top = 0;}~Stack(){cout << "~Stack()" << endl;delete[] p;_capacity = _top = 0;p = nullptr;}};
int main()
{Stack st1;Stack* st2 = new Stack;return 0;
}

如何理解上述的两个new呢?如图所示:
在这里插入图片描述
第一个new是开辟对象的空间,第二个new是开辟对象中p所指的空间!这也就和之前的知识点联系上了,利用delete就是先调用析构,清理对象中的额外资源也就是动态开辟所需的空间!在释放对象的空间!通过这个例子,我们可以更深刻的理解new和delete的工作原理!那么new和delete底层是怎样实现的呢?
运行以下这段代码:

int main()
{Stack* st1 = new Stack;Stack* st2 = (Stack*)operator new(sizeof(Stack));return 0;
}

调试与运行结果如图:
在这里插入图片描述

在这里插入图片描述
我们可以发现通过operator new函数和malloc一样,只是开辟了空间,并不会调用构造函数进行初始化!实际上operator new底层封装的就是malloc!那为什么不直接用malloc呢?那是因为我们要面向对象编程,malloc返回值是0,我们通过operator new是可以抛异常的!更加符合面向对象编程!而operator delete也同理,是封装了free!简单来说既可以用下图来概括!
在这里插入图片描述
在这里插入图片描述

4 定位new表达式

可以通过定位new显式的调用构造函数!在实际应用中,定位new一般是配合内存池使用的,因为内存池分配出来的空间没有初始化,因此如果需要在这块内存池分配出来的空间上构造自定义类型的对象,需要使用定位new显式调用构造函数构造目标对象

格式如下:
new(申请对象地址)类型
new(申请对象地址)类型(类型的初始化列表)

例子如下:

//定位new的使用class A {
private:int _a;int _b;public:A(int a = 10){cout <<"A(int a = 10)"<< endl;_a = a;}A(int a , int b ){cout << "A(int a = 1, int b = 2)" << endl;}~A(){cout << "~A()" << endl;}
};int main()
{A* a1 = (A*)operator new(sizeof(A));//开辟对象的空间new(a1)A;//定位new,显式的调用构造函数,调用默认构造a1->~A();//显式的调用析构函数free(a1);//释放对象空间A* a2 = (A*)operator new(sizeof(A));new(a2)A(10, 20);//定位new,调用两个参数的构造函数a2->~A();free(a2);
}

显式调用了构造函数,那就要显式调用析构函数,要配对使用就可以了!

5 malloc/free和new/delete的区别

相同点:都是从堆上开辟空间,都需要手动的进行释放!
不同之处在于:
1️⃣malloc和free是函数,而new和delete是操作符。
2️⃣malloc不会进行初始化,new开辟空间的同时可以初始化。
3️⃣malloc开辟失败是返回NULL需要我们进行判断,new开辟失败是抛出异常。
4️⃣malloc开辟空间需要手动计算大小并进行传递,而new就可以不用去计算大小!
5️⃣对于自定义类型,malloc不会去调用构造函数,free不会去调用析构函数!而new会去调用构造函数进行初始化,delete会去调用析构函数进行清理!

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

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

相关文章

基于C#实现KMP算法

一、BF 算法 如果让你写字符串的模式匹配&#xff0c;你可能会很快的写出朴素的 bf 算法&#xff0c;至少问题是解决了&#xff0c;我想大家很清楚的知道它的时间复杂度为 O&#xff08;MN&#xff09;&#xff0c;原因很简单&#xff0c;主串和模式串失配的时候&#xff0c;我…

电脑便签工具推荐哪个?电脑上好用的便签软件使用哪一款

对于职场办公人士来讲&#xff0c;一款好用的电脑便签工具可以给日常工作带来极大的便利&#xff0c;如果您的日常工作离不开电脑工具&#xff0c;您就会知晓电脑便签工具在日常工作中的重要&#xff0c;电脑便签通常以一个小的窗口呈现在电脑桌面上&#xff0c;记录一些工作中…

KVM网络环境下vlan和trunk的理解

vmware exsi 平台&#xff0c;虚拟交换机管理界面的上行链路是什么意思 VMware ESXi中的虚拟交换机管理界面中的“上行链路”&#xff08;uplinks&#xff09;是指虚拟交换机连接到物理网络的物理网络适配器。在ESXi中&#xff0c;虚拟交换机&#xff08;vSwitch&#xff09;用…

JAXB:根据Java文件生成XML schema文件

说明 JAXB有个schemagen脚本&#xff0c;可以根据Java文件生成XML schema。这个工具在JAXB独立发布包中有&#xff0c;可以从官网下载JAXB的独立发布包&#xff1a; https://eclipse-ee4j.github.io/jaxb-ri/ 示例 使用schemagen -d <path> <java files>格式 …

安装rabbitMQ

安装rabbitMQ linux安装rabbitMQ centos7下 需要先安装JDK 安装Erlang 需对应版本 安装对应版本的rabbitMQ 参考&#xff1a;CentOS7安装RabbitMQ简单实用教程_普通网友的博客-CSDN博客 启动 systemctl start rabbitmq-server出现的问题 启动失败 Job for rabbitmq-ser…

顺序表和链表

1.线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使 用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串... 线性表在逻辑上是线性结构&#xff0c;也就说是连续的一条直线…

最新最全系列之Selenium:传入webdriver驱动的新方法 Service()函数;以前的executable_path报警告,即将弃用

传入webdriver驱动的新方法 Service()函数&#xff1b;以前的executable_path报警告&#xff0c;即将弃用 以前的方法 举例&#xff1a;webdriver.Chrome(executable_pathdriver_path)&#xff1b;看提示警告&#xff0c;提示该方法即将被弃用&#xff1b;如下图&#xff1a; …

mysql底层是如何存放数据的

总览 首先总的来说&#xff0c;分为四个层级&#xff0c;行页区段。行就是数据库里的一行数据。 但一次从磁盘读进内存的数据量是一页&#xff08;页是读写的单位&#xff0c;默认16KB一页&#xff09;&#xff0c;页分很多种类&#xff0c;例如数据页、溢出页、undo日志页。 …

工业领域的设备“监测”和“检测”有何区别?

在工业领域中&#xff0c;设备的监测和检测是关键的运维活动&#xff0c;它们在保障设备可靠性和生产效率方面发挥着重要作用。尽管这两个术语经常被人们混为一谈&#xff0c;但它们在含义和应用上存在一些关键区别。 "监测"与"检测"的概念 1. 监测&#…

使用wxPython和PyMuPDF合并PDF文档并自动复制到剪贴板

导语&#xff1a;处理大量的PDF文档可能会变得复杂和耗时。但是&#xff0c;使用Python编程和一些强大的库&#xff0c;如wxPython和PyMuPDF&#xff0c;可以使这个任务变得简单而高效。本文将详细解释一个示例代码&#xff0c;展示如何使用这些库来创建一个可以选择文件夹中的…

C#学习相关系列之Linq常用方法---排序(一)

一、构建数据 public class Student_1{public int ID { get; set; }public string Name { get; set; }public int Chinese { get; set; }public int Math { get; set; }public int English { get; set; }public override string ToString(){return string.Format("ID:{0},…

从零开始 通义千问大模型本地化到阿里云通义千问API调用

从零开始 通义千问大模型本地化到阿里云通义千问API调用 一、通义千问大模型介绍 何为“通义千问”&#xff1f; “通义千问大模型”是阿里云推出的一个超大规模的语言模型&#xff0c;具有强大的归纳和理解能力&#xff0c;可以处理各种自然语言处理任务&#xff0c;包括但…

springboot多环境配置

前言 在实际项目研发中&#xff0c;需要针对不同的运行环境&#xff0c;如开发环境、测试环境、生产环境等&#xff0c;每个运行环境的数据库…等配置都不相同&#xff0c;每次发布测试、更新生产都需要手动修改相关系统配置。这种方式特别麻烦&#xff0c;费时费力&#xff0…

ProtoBuf的使用

目录 1.创建.proto文件 1.1文件规范 1.2添加注释 1.3指定proto3语法 1.4package声明符 1.5定义消息(message) 1.6定义消息字段 2.编译contacts.proto文件 3.序列化与反序列化的使用 1.创建.proto文件 1.1文件规范 • 创建.proto文件时&#xff0c;⽂件命名应该使用全…

利用互斥锁解决缓存击穿问题

核心思路&#xff1a;相较于原来从缓存中查询不到数据后直接查询数据库而言&#xff0c;现在的方案是 进行查询之后&#xff0c;如果从缓存没有查询到数据&#xff0c;则进行互斥锁的获取&#xff0c;获取互斥锁后&#xff0c;判断是否获得到了锁&#xff0c;如果没有获得到&am…

DNS如何在Windows NIC配置多个DNS服务器时完成DNS解析查询

使用多个 DNS 服务器 IP 配置 DNS 客户端会增加 DNS 基础结构的容错。所以建议给Windows客户端使用多个 DNS 服务器。 文章目录 什么是DNS&#xff1f;在 NIC 上配置单个 DNS 服务器时&#xff0c;DNS 客户端的解析过程在 NIC 上配置两个 DNS 服务器时&#xff0c;DNS 客户端的…

【Docker】从零开始:3.Docker运行原理

【Docker】从零开始&#xff1a;3.Docker运行原理 Docker 工作原理Docker与系统的关系Docker平台架构图解 Docker 工作原理 Docker与系统的关系 Docker 是一个 Client-Server 结构的系统&#xff0c;Docker 守尹进程运行在王机上&#xff0c; 然后通过 Socket 连接从各尸端坊…

Vue3-shallowRef 和 shallowReactive函数(浅层次的响应式)

Vue3-shallowRef 和 shallowReactive函数&#xff08;浅层次的响应式&#xff09; shallowRef函数 功能&#xff1a;只给基本数据类型添加响应式。如果是对象&#xff0c;则不会支持响应式&#xff0c;层成也不会创建Proxy对象。ref和shallowRef在基本数据类型上是没有区别的…

第1关:图的邻接表存储及求邻接点操作

任务要求参考答案评论2 任务描述相关知识编程要求测试说明 任务描述 本关任务&#xff1a;要求从文件输入顶点和边数据&#xff0c;包括顶点信息、边、权值等&#xff0c;编写程序实现以下功能。 1&#xff09;构造图G的邻接表和顶点集&#xff0c;即图的存储结构为邻接表。 …

SpringCloud原理-OpenFeign篇(二、OpenFeign包扫描和FeignClient的注册原理)

文章目录 前言正文一、从启动类开始二、EnableFeignClients 的源码分析三、Import FeignClientsRegistrar 的作用四、FeignClientsRegistrar#registerFeignClients(...)五、饥饿注册&懒注册 FeignClientsRegistrar#registerFeignClient(...)六、通过Holder真正注册beanDefi…