Learning C++ No.8【内存管理】

news2024/9/21 11:02:44

引言:

北京时间:2023/2/12/18:04,昨天下午到达学校,摆烂到现在,该睡睡,该吃吃,该玩玩,在一顿操作之下,目前作息调整好了一些,在此记录,2月11,开学之日,是比较搞笑、难忘的一天。简洁记录之后,此时我们就开始新知识的学习,一起来看一看什么是C++中的内存管理。
在这里插入图片描述

C++中的内存管理

复习C语言中的内存管理

谈到内存管理,我们必须要想到的就是内存的三个基本大区,栈区、堆区、静态区,但当我们一想到这些东西的时候,我们还应该想到的就是,C语言中的动态内存规划,谈到动态内存规划,此时我们就应该想到其中的4大天王,4个有关动态内存规划的函数,malloc、calloc、realloc,所以接下来我们就来复习看一下这三个函数的使用和特性。

函数使用方法和特性
malloc:int*p1 = (int*)malloc(10*sizeof(int));不会初始化空间
calloc:int*p1 = (int*)calloc(10,sizeof(int));会初始化空间
realloc:int*p2 = (int*)realloc(p1,40);用于扩容场景
free:释放我动态开辟的空间

搞定了C语言中的知识之后,此时我们就开始我们C++中有关内存管理知识的学习啦!首先在我们内存中,我们的常量区也叫作数据区,并且我们的栈区是从高地址向低地址存放数据的,我们的堆区是从低地址向高地址存放数据的,如图所示:

所以此时我们就带着这幅图去学习一下什么C++中的内存规划,C++中的内存规划主要就是涉及到两个关键字,new和delete搞定了这两个关键字的特性和使用方法,我们就把C++中的内存规划给学的差不多了,首先,C++中为什么要提出new和delete这两个关键字呢?其实目的主要就是因为在C语言中并没有专门针对于自定义类型使用的内存开辟函数和释放函数,有的只是针对于内置类型的函数(malloc、realloc、malloc、free),并没有专门给自定义类型使用的函数,所以在我们的C++中有了类和对象的概念之后,自定义类型变得是更加的重要,所以为了专门给自定义类型的内存申请和释放,C++中就提出了new和delete这两个函数,并且我们的new和delete不仅针对自定义类型可以使用,对我们内置类型也是同样适用的,例如:int* p1 = new int;意思就是开辟一个整形类型给p1指针,但注意,此时的该内存是没有被初始化的,只有这样写:int* p1 = new int(10);此时的该整形内存才会被初始化为10,所以我们的new关键字开辟的空间,不管是自定义类型还是内置类型,此时你自己都是可以决定要不要进行初始化,决定初始化的值到底是给多少,所以这就是new的好处,不仅可以对所有类型开辟空间,还可以决定初始化注意:此时上句代码一定要区别于下面这句代码:int* p1 = new int[10]上述代码的意思是开辟一个整形空间给p1,并且将该空间初始化为10,而该句代码的意思却是,开辟10个整形空间给p1,并且无初始化效果,若你想要有初始化效果,那么此时你就可以写成这样:int* p1 = new int[10] {1,2,3,4,5,6,7,8,9,10};

具体使用如图所示:

并且此时看见上述的代码是,我们要意识到,我们的new和delete要配套使用,malloc和free要配套使用,不可以交叉使用,不然会出问题。

总:new对于自定义类型的使用就是new+类型+圆括号(初始化)或者中括号(个数),对于自定义类型的使用就是,new单个对象就是调用构造函数,new多个对象就是调多次构造函数,所以new/delete和malloc/free最大的区别就是它们对于自定义类型使用时,new会自动调用构造函数,delete会自动调用析构函数。

operator new 和operator delete的使用

new是用户,也就是我们写代码的时候进行动态内存申请和释放的操作符,operator new和operator delete是系统提供的全局函数(并不是什么重载,就是一个库里的函数而已),也就是编译器使用的函数,所以new申请空间使用的就是底层函数operator new,delete释放空间使用的就是底层函数operator delete,并且此时operator new 实际上使用的却又是malloc来申请空间,operator delete实际上使用的是free来释放空间,所以operator new和operator delete本质上就是malloc和free的封装。搞懂了这些,此时我们就再来看一下,C++中为什么要有new和delete

再谈new出现的意义
1.要满足空间申请(因为C语言中已经有该功能,所以申请空间的功能就不需要重新实现了,直接原样的使用malloc就行)
2.满足自动调用构造函数去初始化自定义类型(C语言中没有该功能,所以该功能是需要自己去实现的,所以此时就要在malloc的基础之上再加上这一功能,这也就是new出现的原因)

所以在上述的基础上,我们就知道了new出现的原因,就是帮助用户去使用C语言中的malloc功能,并且实现自动调用构造函数的功能的一个封装函数。并且此时因为malloc失败之后返回的是空指针,不符合我们面向对象的过程,所以我们如果想要失败之后返回的是异常的话,就需要重新去写一个函数出来,该函数就是operator new,这也就是operator new出现的原因。所以operator new就是为了封装malloc前提下,也可以实现自动调用构造函数和开辟失败之后可以返回一个异常给我们而不是返回空指针的作用,这些适合自定义类型使用的开空间特性。所以这就是operator new出现的原因,当然此时的operator delete是同理如此使用的。只是此时delete在使用之前必须先调用析构函数而已,只有把所有的空间资源调用析构函数释放完了之后,delete才会去释放该对象,原理和new调用operator new是一样的,本质就是去调用free函数和malloc函数。

并且此时除了operator new和operator delete 还有一个operator new[];
一个原理,此时的调用原则就是:operator nwe[ ]->operator new->malloc

new和delete对于内置类型和自定义类型的区别

好的,搞懂了这些,此时我们就来看一下new和delete对于内置类型和自定义类型的区别,主要就是为了研究一下怎么写代码才不会出问题,到底需不需配套使用,例如:此时我有一个栈,我用这个栈在main函数中创建了一个局部对象 st(Stack st),此时的这个对象是因为它是一个自定义类型,所以此时它是不需要我自己去释放的,因为自定义类型都会去自己调用构造函数和析构函数,只有内置类型才是因为栈帧的销毁而销毁的,但一定要去注意指针这个内置类型, 因为指针变量都是一个内置类型的变量,无论是什么类型的指针都是一样的,例如:Stack* ptr和int* p1,此时的ptr和p1在本质上都是一个内置类型对象,所以不需要我们自己去销毁,它会自己随着栈帧的销毁而销毁,明白了这个东西,此时重点就来了。
重点:虽然如上述所说,指针都是内置类型,会自己销毁,但是指针指向的那块空间是不会自己销毁的,所以此时如果你不仔细做处理的话,此时你的程序就会因为没有释放内存而崩溃,所以,当我们创建了一个指针变量的时候,就一定要考虑到,指针指向的空间存放在什么位置,例如:此时的该栈,栈中的数据中有一个数组,此时的该数组是一个动态数组,是通过new关键字开辟出来的,所以此时可以明显的知道,该空间是在堆区上的,所以此时ptr指针指向的该空间是不会自己释放的,所以需要我们进行手动释放,例如:(delete ptr;),所以此时一个自定义类型的指针ptr,例如:Stack* ptr = new Stack;此时就不可以像Stack* st;一样,想要使用free和delete都可以释放指针指向的空间。因为Stack st;并没有多余的空间需要清理,此时它只要把在栈中的成员变量中的那个指针,new开辟出来的那块动态数组的空间清理掉就可以了(因为该空间是栈中的new开辟出的一个动态数组),所以free可以直接把st对象中的那块Stack中的动态开辟的数组空间直接给释放掉。然而,我们的Stack* ptr = new Stack;此时指针ptr是在内存中的栈帧上的,但ptr指向了一块new出来的Stack,该Stack是在堆区的,并且Stack中还有一个指针,该指针指向的空间又是new出来的,所以此时这块空间也是在堆区上的,所以此时ptr指向的空间又指向了一块空间,所以此时不可以直接free(ptr),会有内存泄露问题,只有把Stack中new出来的空间给先释放才可以把ptr指向的空间释放,所以此时就不可以使用free去直接释放,而是要使用delete ptr;去释放,因为delete会先去调用析构函数(但是前提是该指针指向的空间的类型为自定义类型),把ptr指针指向的空间中又开辟的空间给先析构掉,然后再去operator delete,调用free,去释放ptr指针指向的空间,但如果上述的情况你写成free ptr;也是可以的,只是少调用了一次析构函数而已,然后造成内存泄露的问题,因为在C和C++中并不会帮助我们检查内存泄露问题,所以综上,我们在释放内存的时候,一定要匹配使用,并且并不是自定义类型就一定有问题,主要是还要看该自定义类型中是否存在指针指向的另一块空间。

浅浅的摸一下泛型编程

从模板看泛型编程

写了这么久的代码,我们知道在C++中有一个东西叫函数重载,可以让我们把一个函数名(Swap)给重复使用,通过参数类型的不同来区别函数的不同,但是会发现,有一种类型的参数,我就需要写一个该类型的Swap函数,这样是比较麻烦的,所以C++中就又提出了模板的概念,该概念的意思就是可以让我们实现一个与类型无关的Swap函数,别的函数就通过该模板进行使用,就是相当于存在一个模具,通过给这个模具填充不同的材料(类型),来获得不同的铸件(既生成具体类型的代码),这样就可以节省很多的代码量了,所以总的来说:泛型编程就是编写与类型无关的通用代码,是代码复用的一种手段,模板就是泛型编程的基础。

什么是模板

首先模板分成两类,一个是函数模板,一个是模板类

函数模板
类模板

什么是函数模板

概念:函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
具体模板的实现:如图

注意:此时虽然在调试期间,我们看到的是同一个函数,但是只是编译器处理过之后的,本质在系统内部,是两个不同的函数,从栈帧的大小和汇编代码这些方面都是可以看出来的。

并且此时我们知道在C++类和对象中,有一个对象实例化的过程,所以此时模板的使用也是有一个模板实例化的过程的,此时的该过程的理解,就可以让我们更好的理解为什么模板使用的是两个不同的函数,如图
在这里插入图片描述
模板实例化的一些小问题:

主要是分为自动推演模板类型和显示实例化两种
什么是自动推演模板类型:例, cout << Add((double)b, d) << endl;cout << Add(a, (int)c) << endl;此时就是我们自己去进行强制类型转换,把不是同类型的两个数据给转换成同一类型,这样才可以去实现模板,不会出现无法识别模板的问题,

显示实例化: cout << Add<double>(a, c) << endl; cout << Add<int>(b, d) << endl;这两句代码就是显示实例化,直接把该数据的类型写在函数名的后面,确定了该函数数据的类型,此时这样模板就可以很好的识别参数了。

如图

template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}

int main()
{
	int a = 1, b = 2;
	double c = 1.1, d = 2.2;
	swap(a, b);
	swap(c, d);

	//实参传递给形参,自动推演模板类型
	cout << Add((double)b, d) << endl;
	cout << Add(a, (int)c) << endl;

	//显示实例化
	cout << Add<double>(a, c) << endl;
	cout << Add<int>(b, d) << endl;

	return 0;
}

并且此时强调,我们的库中已经用模板给我们实现了一个swap函数,所以以后我们可以直接使用
并且对于非模板函数和同名函数模板,如果其它条件都相同,在调用时会优先调用非模板函数而不会从该模板产生出一个实例,如果模板可以产生一个具有更好匹配的函数,那么选择模板(如类型不相同的两个数据);并且模板函数是不允许自动类型转换的,只有普通函数才可以进行自动类型转换。
如图:

什么是类模板

使用模板类,直接就可以把我们以前使用的typedef的方法给淘汰掉了,模板类直接就可以搞定每个数据结构的类型,直接使用显示实例化的方式,把该数据结构的类型给确定,根本不需要去改什么typedef定义的类型,一个尖括号(<>) 直接搞定了,剩下的交给编译器去完成就行了。
如图:

强调:类模板只能使用显示实例化的方式去使用

在这里插入图片描述

总结:C++这块老腊肉我们又小小的啃了一口,所以继续加油吧!

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

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

相关文章

C++基础(6) - 复合类型(下)

文章目录指针1、指针概述1.1 存储器和存储地址空间1.2 内存地址1.3 指针和指针变量2、声明和初始化指针变量2.1 指针变量的声明2.2 指针变量的初始化3、使用指针变量3.1 解除引用3.2 野指针和空指针4、指针的宽度和跨度4.1 自身类型和指向类型4.2 指针变量所取内容的宽度4.3 指…

chatGPT会是银弹吗

chatGP最近火的一塌糊涂&#xff0c;它通过语言生成技术和自然语言处理能力&#xff0c;帮助用户快速解决问题并生成内容。目前&#xff0c;这款工具现在已经拥有超过一亿的活跃用户&#xff0c;并且因其高效率和易用性而受到了广大用户的好评。 不过谷歌可就倒霉了&#xff0c…

Shells:一款功能强大的反向Shell快速生成工具

关于Shells Shells是一款功能强大的反向Shell快速生成工具&#xff0c;该工具由4ndr34z负责开发和维护&#xff0c;可以帮助广大研究人员轻松生成常用的反向Shell。如果你需要一种简单的方法来生成格式化的PowerShell以及Python反向Shell的话&#xff0c;Shells这款工具将是你…

【IPD】敏捷开发与IPD结合的实践培训课程「3月11-12日」

课程名称敏捷开发与 IPD结合的实践 (Agile Development - IPD and Agile Development Practice &#xff09;参加对象企业总工、技术总监、系统架构师、研发经理、测试经理、质量/品质经理、研发测试骨干&#xff0c;以及研发测试技术人员。课程背景软件系统的日益复杂化和用户…

C语言学习笔记-内存管理

这篇将讲解 C 中的动态内存管理。C 语言为内存的分配和管理提供了几个函数。这些函数可以在 <stdlib.h> 头文件中找到。 序号函数和描述1void calloc(int num, int size);在内存中动态地分配 num 个长度为 size 的连续空间&#xff0c;并将每一个字节都初始化为 0。所以…

2023的金三银四,测试员还能找到好工作吗?

按照往年的惯例&#xff0c;春节后复工的 3 月、4 月是人员跳槽最频繁的时候&#xff0c;俗称“金三银四”。然而&#xff0c;市场大环境的影响&#xff0c;很多行业感受到了一丝寒冷的气息。 我们以为受影响比较轻的互联网行业&#xff0c;头上也充满乌云&#xff0c;所谓互联…

ROS2机器人编程简述humble-第四章-BASIC DETECTOR .3

书中程序适用于turtlebot、husky等多种机器人&#xff0c;配置相似都可以用的。支持ROS2版本foxy、humble。基础检测效果如下&#xff1a;由于缺&#xffe5;&#xff0c;所有设备都非常老旧&#xff0c;都是其他实验室淘汰或者拼凑出来的设备。机器人控制笔记本是2010年版本。…

九龙证券|本周5只新股申购,特斯拉、蔚来、理想的供应商来A股了!

据现在组织&#xff0c;2月13日到17日共有5只新股申购&#xff0c;其间上证主板2只&#xff0c;深证主板1只&#xff0c;北交所2只。 2月14日发动打新的深证主板新股多利科技成立于2010年&#xff0c;是一家专心于轿车冲压零部件及相关模具的开发、出产与出售的企业。从2020年…

nodejs版本管理器nvm下载,安装详情

文章目录前言一、NVM下载二、NVM安装三.使用NVM安装nodejs1.NVM常用命令2.安装node3.使用node前言 安装nodejs方式有两种。 第一种&#xff1a;官网下载  通过nodejs官网https://nodejs.org/zh-cn/下载安装 &#xff0c;但有个缺陷&#xff0c;不同版本的nodejs无法顺利的切…

软件测试面试理论(超详细)

【面试理论知识】1、你的测试职业发展是什么? 测试经验越多&#xff0c;测试能力越高。所以我的职业发展是需要时间积累的&#xff0c;一步步向着高级测试工程师奔去。而且我也有初步的职业规划&#xff0c;前3年积累测试经验&#xff0c;按如何做好测试工程师的要点去要求自己…

Dubbo中应用级,与接口级配置中心的使用,包括单配置中心与多配置中心

接口级或应用级服务发现 Dubbo3 默认采用 “应用级服务发现 接口级服务发现” 的双注册模式 可以通过配置 dubbo.registry.register-modeinstance/interface/all 来改变注册行为。 instance &#xff1a; 应用级interface &#xff1a; 接口级all &#xff1a;两者都注册&a…

一文详解jvm之-Xms -Xmx -Xmn -Xss -XX:PermSize -XX:MaxPermSize等参数的设置和优化以及如何选择垃圾回收器

文章目录1. 文章引言2. 常见配置汇总2.1 Xmn Xms Xmx Xss的区别2.2 其他常见配置2.3 典型设置举例3. 回收器选择3.1 吞吐量优先的并行收集器3.2 响应时间优先的并发收集器3.3 辅助信息4. 参考文档1. 文章引言 我们经常在tomcat的catalina.bat或者catalina.sh中配置如下参数&am…

亚马逊、速卖通、temu、Cdiscount通过自养号给自己店铺测评补单需要哪些技巧?

亚马逊卖家通过测评平台&#xff0c;获取亚马逊买家的真实服务点评&#xff0c;即亚马逊测评。它既可以让买家更加快速、有效地了解产品&#xff0c;也可以让卖家有机会通过买家的评论去优化产品&#xff0c;以获得更多买家的喜爱。因此&#xff0c;亚马逊测评之于卖家&#xf…

Linux下内存buff/cache占用过多问题解决

在Linux下经常会遇到buff/cache内存占用过多问题&#xff0c; 尤其是使用云主机的时候最严重&#xff0c;由于很多是虚拟内存&#xff0c;因此如果buff/cache占用过大的&#xff0c; free空闲内存就很少&#xff0c;影响使用&#xff1b; 通常内存关系是&#xff1a; 普通机器…

Python 获得摄像头捕捉的图像

Python 获得摄像头捕捉的图像 很多时候&#xff0c;我们都需要通过摄像头捕获图像&#xff0c;以便进行处理&#xff0c;在这里分享的是通过OPEN CV这个库来实现。 OPEN CV的安装和使用 安装很简单&#xff0c;相关文章也很多&#xff0c;注意一点&#xff0c;不要安装最新版…

【Android视频号信息获取①】

*在2019年深圳上班的时候 那时候还是个Java 码农 接触了一下 Xposed.时隔多年 忘记差不多了 用frida先来练练手 新公司又让我研究微信视频号获取个人的视频主页标题列表 * 确定微信版本 不同版本微信hook点不一样。 预想实现方式 用Xposed去请求注册一个中转服务 然后脚本请…

Java——编辑距离

题目链接 leetcode在线oj题——编辑距离 题目描述 给你两个单词 word1 和 word2&#xff0c; 请返回将 word1 转换成 word2 所使用的最少操作数 。 你可以对一个单词进行如下三种操作&#xff1a; 插入一个字符删除一个字符替换一个字符 题目示例 输入&#xff1a;word…

搭建DJI 无人机Onboard SDK ROS开发环境及测试

搭建DJI 无人机Onboard SDK ROS开发环境及测试功能包简介开发环境搭建测试功能包连接设备启动SDK功能包简介 ROS功能包名称&#xff1a;dji_sdk 功能包功能&#xff1a;用于DJI 板载SDK的ROS版本 OSDK 是一个用于开发无人机应用程序的开发工具包&#xff0c;基于OSDK 开发的…

CUDA线程层次一文搞懂|参加CUDA线上训练营

设备术语 Host&#xff1a;CPU 和 内存 (host memory)Device&#xff1a;GPU 和显存 (device memory) CUDA 线程层次 CUDA 线程层次分为&#xff1a; Thread 所有线程执行相同的核函数并行执行 Thread Block 执行在一个 Streaming Multiprocessor &#xff08;SM&#xff09…

Python快速上手系列--异常处理--详解篇

本章所说的就是我们经常遇到的一个问题&#xff0c;报错、异常。我们应该如何处理&#xff0c;让它不影响后面的程序运行。异常首先我们看看一个简单的示例。print(2/0)其结果可想而知&#xff0c;当然是报错了&#xff01;程序被终止了&#xff01;这里会提示用户&#xff0c;…