C++学习笔记—— C++内存管理方式:new和delete操作符进行动态内存管理

news2024/9/24 13:25:35

系列文章目录

http://t.csdnimg.cn/d0MZH

目录

  • 系列文章目录
    • http://t.csdnimg.cn/d0MZH
  • 比喻和理解
    • a.比喻
      • C语言开空间
      • C++开空间
    • b.理解
      • a、C语言的内存管理的缺点
          • 1、开发效率低(信息传递繁琐)
          • 2、可读性低(信息展示混乱)
          • 3、稳定性差(开空间可能失败)
          • 代码演示
      • b、C++的内存管理方式的优点
          • 1、开发效率高、稳定
          • 2、可读性高(信息集中、整洁)
  • 一、C++又提出了自己的内存管理方式——通过new和delete操作符进行动态内存管理
    • 1. 我们在建开辟空间时会发现我们有4个核心需求:
    • 2. C++将开辟和释放分别集中在new和delete两个操作符实现:
    • 3. new集成:malloc、抛异常、构造函数;delete集成free、抛异常、析构函数
    • 4.new和delete内含的功能会根据具体的情况选择性发挥作用
  • 二、 operator new与operator delete函数
    • 1、 operator new与operator delete函数
  • 三、内置类型
  • 四、自定义类型
    • 1、new的原理
    • 2、delete的原理
    • 3、new T[N]的原理
    • 4、delete[ ]的原理
  • 五、delete[ ]底层原理
  • 六、定位new表达式(placement-new)
    • 使用场景:


比喻和理解

C语言无法方便进行内存管理,C语言有关空间的所有操作都充满了冗余操作;
而C++通过new和delete操作符进行动态内存管理。
简而言之,C++对内存管理的创举主要是让我们输入的信息更高效的被编译器理解。

a.比喻

C语言开空间

打个比方,C语言开辟空间就像是:你要开席,你叫C语言去买10瓶可乐,C语言会做下面这些事:

  1. 问无效信息;
    它一次只买一瓶,重复十次买一瓶的操作,它每买一瓶都问你去哪家店买、走哪条路;

  2. 不会思考;
    买可乐的预算有多少,购买策略要你全部说清楚,购买时遇到意料之外的情况就打电话问你;

  3. 轻易放弃,结果不上报;
    如果C语言在去的第一家商店购买失败,它就不买了,也不会主动报告购买失败,需要我们专门询问,要是不问C语言就放任错误发生;

C++开空间

而C++就像是:你叫C++去买10瓶可乐,它会问你一些信息,然后再做以下事情:

  1. 主动思考
    C++会问你给它多少预算,给多少人喝,每人喝几杯,购买现场它根据这些信息自己决定购买策略。

  2. 灵活多变
    如C++在商店购买失败,它会自动换一家商店买。

  3. 有责任感
    换商店会一直换到成功为止,除非彻底买不到才打电话上报。


b.理解

a、C语言的内存管理的缺点

1、开发效率低(信息传递繁琐)
  1. 内存空间开辟需要显示定义
  2. 内存空间开辟代码编写繁琐
2、可读性低(信息展示混乱)
  1. 代码修改检查要反复对比
  2. 代码开空间显示不整齐
3、稳定性差(开空间可能失败)
  1. 就算声明了开空间,也不确定有没有开空间
  2. 开空间失败不自动报警,需要我们设置报警
代码演示

如下代码,我们的计划实现:

  1. 开一块空间存 int 类型的数据;
  2. 开辟这块空间并且初始化;
  3. 在原空间的基础上扩容;

为了实现以上功能,我们要写大量和我们的意图没有任何关系的内容。

代码演示:用C语言的方法开辟空间,可以看到这些代码是非常冗余的:

void Test()
{
	int* p1 = (int*)malloc(sizeof(int));	//开辟一个int大小的空间
	int* p2 = (int*)calloc(4, sizeof(int));	//开4个int大小的空间并且初始化为0
	int* p3 = (int*)realloc(p2, sizeof(int) * 10);	//扩容,把大小扩大到10个int,并且转移空间地址到p31
}

糟糕的是上面三行代码如果开空间失败不会自动报警


b、C++的内存管理方式的优点

在实践时可以发现,我们编码时思维聚焦于开辟空间的用途
同时也发现,实现功能时会反复使用同样的空间大小和变量类型。

1、开发效率高、稳定

如下代码,我们要修改开辟的空间属性,只需要在开空间的代码上微调即可;

void Test()
{
  // 动态申请一个int类型的空间
  int* ptr4 = new int;
  
  // 动态申请一个int类型的空间并初始化为10
  int* ptr5 = new int(10);
  
  // 动态申请5个int类型的空间,并初始化为0
  int* ptr6 = new int[5];

  // 动态申请5个int类型的空间并初始化前3个空间,后面2个空间默认为0
  int* ptr7 = new int[5]={123};

  delete ptr4;
  delete ptr5;
  delete[] ptr6;
  delete[] ptr7;
}

简而言之,开辟一个空间,进行一番操作初始化它,就是我们的常做的操作;
由此,通过自定义类型就可以实现:通过一个类我们可以在开辟一个空间的同时启动一个构造函数对这个空间进行操作;
如下代码:
我们定义了一个自定义类型A,当我们使用new来开辟空间时,会自动启动A的构造函数;

#include<iostream>
using namespace std;
static int i = 1;

class A
{
public:
	A(int a = 0)
		: _a(a)
	{
		cout << "A(" << i++ << "):" << this << endl;
	}

	~A()
	{
		cout << "~A():" << this << endl;
	}
private:
	int _a;
};

int main()
{
	// new/delete 和 malloc/free最大区别是 
	// new/delete对于【自定义类型】除了开空间,还会调用构造函数和析构函数
	A* p4 = new A(1);	// 开辟一个int空间
	A* p5 = new A[10]{1,2,3,4,5};
	A* p6 = new A[10];

	delete p4;
	delete[] p5;
	delete[] p6;
	return 0;
}


2、可读性高(信息集中、整洁)

在这里插入图片描述


一、C++又提出了自己的内存管理方式——通过new和delete操作符进行动态内存管理

1. 我们在建开辟空间时会发现我们有4个核心需求:

开辟空间(1、2),释放空间(3、4):

  1. 开辟空间;
  2. 初始化空间;
  3. 释放空间;
  4. 指针置空

2. C++将开辟和释放分别集中在new和delete两个操作符实现:

new实现:
1.开辟空间、2.初始化空间;

delete实现:
3.释放空间、4.指针置空;

在申请自定义类型的空间时,new会自动调用operator new和构造函数,delete会自动调用operator delete 和 析构函数。

3. new集成:malloc、抛异常、构造函数;delete集成free、抛异常、析构函数

  1. new 是operator new 和构造函数的结合, delete 是oprator delete 和析构函数的结合;
  2. perator new 是malloc和抛异常的结合,oprator delete 是free和抛异常的结合。

4.new和delete内含的功能会根据具体的情况选择性发挥作用

  1. 在new 起作用的过程中,固定发挥malloc、抛异常、构造函数初始化空间;
  2. 在delete 起作用的过程中,固定发挥free、抛异常的作用,根据情况来判断是否调用析构函数 释放空间;

如果开空间时没有malloc开辟空间,则当我们要释放空间时,我们可以去使用free或delete释放空间,因为此时delete只有free可以发挥作用,没有调用析构函数的必要;

如下代码:对p1象占用了int类型的空间,我们使用free就可以释放这个空间;
相同情况下,p2使用delete也是可以的;
同理,p3的int【10】也是free、delete皆可。

#include<iostream>
using namespace std;

class A
{
public:
	A(int a = 0)
		:_a(a)
	{
		cout << a << "构造" << endl;
	}

private:
	int _a;
};

int main()
{
	// new和delete内含的功能会根据具体的情况选择性发挥作用
	A* p1 = new A(1);	//这里p1使用了int空间,我们delete或free释放即可
	A* p2 = new A(1);	//这里p2使用了int空间,我们delete或free释放即可
	free(p1);
	delete p2;

	A* p3 = new A[10];
	delete[] p3;



	return 0;
}

二、 operator new与operator delete函数

1、 operator new与operator delete函数

new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。

operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。

如下代码我们尝试通过new来创建一个对象


#include<iostream>
using namespace std;

typedef char DataType;
class Stack
{
public:
	Stack(size_t capacity = 4)
	{
		cout << "Stack()" << endl;

		_array = new DataType[capacity];
		_capacity = capacity;
		_size = 0;
	}

	void Push(DataType data)
	{
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}

	~Stack()
	{
		cout << "~Stack()" << endl;
		_array = nullptr;
		_size = 0;
		_capacity = 0;
	}

private:
	// 内置类型
	DataType* _array;
	int _capacity;
	int _size;
};

Stack* func()
 {
	int n;
	cin >> n;
	Stack* pst = new Stack(n);
	return pst;
}

int main()
{
	Stack* ptr = func();
	ptr->Push(1);
	ptr->Push(2);
	delete ptr;
	return 0;
}

在这里插入图片描述

三、内置类型

如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:

  1. new/delete申请和释放的是单个元素的空间;
  2. new[ ]和delete[ ]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。

四、自定义类型

1、new的原理

  1. 调用operator new函数申请空间
  2. 在申请的空间上执行构造函数,完成对象的构造
	A* p1 = new A(1);

2、delete的原理

  1. 在空间上执行析构函数,完成对象中资源的清理工作
  2. 调用operator delete函数释放对象的空间
	delete p1;

3、new T[N]的原理

  1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请
  2. 在申请的空间上执行N次构造函数
  3. 返回开辟空间的地址
	A* p2 = new A[10];

4、delete[ ]的原理

  1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
  2. 调用operator delete[]释放空间,实际在operator delete[]中调用数个operator delete来释放空间
	delete[] p2;

五、delete[ ]底层原理

delete[]被设计用来释放多个连续的同构造空间,那么它需要获取两个信息:

  1. 被释放空间的位置——在delete[]后接的地址p2;
  2. 调用几次析构函数——在我们使用new[]的时候,会自动在存储信息的空间前面开一个空间用来记录该空间存储了多少个对象。

如下代码:

	A* p2 = new A[10];
	delete[] p2;

我们使用了new A[10]开辟了十个存储int数据的空间,我们的指针p2也指向了这个空间的第一个元素的地址;
而当我们调用delete[]来释放该空间时,delete[ ]空着的[ ]就会把p2前面四个字节的内容作为整形装到[]里面,于是在编译器看来 delete[] p2 就变成了 “ delete[10] p2” ;
由此编译器知道了需要调用十次析构函数释放该空间,同时p2也被修改指向了该空间真正的起始地址,最后释放空间时会把开头的4个字节和后面的40个字节一起释放掉,然后p2被置空。至此new A[10]所占用的空间被全部释放;

在这里插入图片描述

六、定位new表达式(placement-new)

定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
使用格式:

new (place_address) type

或者

new (place_address) type(initializer-list)

place_address必须是一个指针,initializer-list是类型的初始化列表

使用场景:

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

即,有些情况下编译器会使用内存池来优化运行效率,而这时我们要开辟空间时就会出现空间开辟到内存池上而不是堆上,如此一来开辟空间就相当于失败了,因为内存池不能像堆一样一直保存信息;
所以有了placement-new的概念,专门在那些使用内存池的编译器上发挥作用,特别地要求开辟空间要在堆上开辟而不是内存池上。

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

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

相关文章

【MATLAB】基于VMD分解的信号去噪算法(基础版)

代码的使用说明 基于VMD分解的信号去噪算法&#xff08;基础版&#xff09; 代码流程图 代码效果图 本文代码&#xff1a;阿里云盘分享 获取代码请关注MATLAB科研小白的个人公众号&#xff08;即文章下方二维码&#xff09;&#xff0c;并回复VMD去噪 本公众号致力于解决找代…

铭飞CMS list 接口 SQL注入漏洞复现

0x01 产品简介 铭飞CMS是一款基于java开发的一套轻量级开源内容管理系统,铭飞CMS简洁、安全、开源、免费,可运行在Linux、Windows、MacOSX、Solaris等各种平台上,专注为公司企业、个人站长快速建站提供解决方案 0x02 漏洞概述 铭飞CMS在5.2.10版本以前list 接口处存在sql注入…

AI抽烟识别系统研发关键

为了设计一个有效的AI抽烟识别系统&#xff0c;我们需要考虑几个关键组成部分&#xff1a;图像捕捉、数据处理、模型训练、以及实际应用场景。下面是这个方案的详细阐述&#xff1a; 1. 图像捕捉与数据收集 摄像头部署&#xff1a;首先&#xff0c;在需要监控的区域安装高分辨…

社交网络分析1:起源发展、不同领域的应用、核心概念

社交网络分析1&#xff1a;社交网络相关定义和概念 写在最前面关于课程 社交网络、社交网络分析社交网络发展阶段&#xff08;自己感兴趣&#xff09;1. 社交网络的起源2. 社交网络的演变3. 社交网络的成熟4. 发展阶段补充和展望 2023社交大变革&#xff08;自己感兴趣的点&…

带你手把手解读firejail沙盒源码(0.9.72版本) (三) fcopy

文章目录 main.c该模块的各个函数功能详解selinux_relabel_pathcopy_filemkdir_attrcopy_linkproc_pid_to_selffs_copydircheckduplicate_dirduplicate_fileduplicate_linkmain Makefile main.c 文件总结 ├── fcopy │ ├── Makefile │ └── main.cmain.c #include…

深度学习中的高斯分布

1 高斯分布数学表达 1.1 什么是高斯分布 高斯分布(Gaussian Distribution)又称正态分布(Normal Distribution)。高斯分布是一种重要的模型&#xff0c;其广泛应用与连续型随机变量的分布中&#xff0c;在数据分析领域中高斯分布占有重要地位。高斯分布是一个非常常见的连续概…

【Spring】03 容器

文章目录 1. 定义2. BeanFactory1&#xff09;惰性加载2&#xff09;基本的容器功能3&#xff09;XML配置 3. ApplicationContext1&#xff09;主动加载2&#xff09;AOP支持3&#xff09;事件发布与监听4&#xff09;国际化支持5&#xff09;注解支持 4. Spring容器的生命周期…

正则表达式(6):分组与后向引用

正则表达式&#xff08;6&#xff09;&#xff1a;分组与后向引用 总结 本博文转载自 在本博客中&#xff0c;”正则表达式”为一系列文章&#xff0c;如果你想要从头学习怎样在Linux中使用正则&#xff0c;可以参考此系列文章&#xff0c;直达链接如下&#xff1a; 在Linux中…

ThinkPHP连接ORACLE数据库教程

目录 概念基本步骤详细操作问题排除参考 概念 要连接Oracle数据库&#xff0c;必须有两个东西&#xff0c;一个PHP官方写的扩展&#xff0c;一个Oracle官方写的客户端PHP是通过扩展去操作oralce客户端连接的服务端数据库&#xff0c;所以两个都不能少&#xff0c;而且版本必须…

arthas统计大循环方法时的注意事项

背景 arthas是我们日常查找各种问题的利器&#xff0c;不过我们也需要意识到arthas本身也是有性能损耗的&#xff0c;所以当老板对你提问为什么使用arthas分析时这个方法比生产上正常运行时这个方法的耗时要长很多&#xff0c;你可以向他进行解释&#xff0c;进而由于arthas的…

边缘检测@获取labelme标注的json黑白图掩码mask

import cv2 as cv import numpy as np import json import os from PIL import Imagedef convertPolygonToMask(jsonfilePath):

MySQL——数据类型

目录 一.数据类型分类 二. 数值类型 1.tinyint类型 2.bit类型 3.float类型 4.decimal 三.字符串类型 1.char 2.varchar 四.日期和时间类型 五.enum和set 一.数据类型分类 关于数据库的数据类型有非常多&#xff0c;但是并非所有的数据类型都是我们常用的&#xff…

《信息技术时代》期刊杂志论文发表投稿

《信息技术时代》期刊收稿方向&#xff1a;通信工程、大数据、计算机、办公自动化、信息或计算机教育、电子技术、系统设计、移动信息、图情信息研究、人工智能、智能技术、信息技术与网络安全等。 刊名&#xff1a;信息技术时代 主管主办单位&#xff1a;深圳湾科技发展有限…

vs2019比较两个代码的区别方法

vs2019比较两个代码的区别方法 效果代码 效果 代码 Tools.DiffFiles 3d_mig(1).c 3d_mig_xin0.c

UDP内网穿透和打洞原理与代码实现

1、众所周知&#xff0c;现在主流网络用的还是IPV4协议&#xff0c;理论上一共有2^3243亿个地址&#xff0c;除去私有网段、网络ID、广播ID、保留网段、本地环回127.0.0.0网段、组播224.0.0.0网段、实际可用就是36.47亿个&#xff1b;全球的服务器、PC机、手机、物联网设备等需…

08.CSS盒模型

CSS盒模型 1.介绍 CSS 会把所有的 HTML 元素都看成一个盒子&#xff0c;所有的样式也都是基于这个盒子 2.盒模型构成 介绍 margin&#xff08;外边距&#xff09;&#xff1a;盒子与外界的距离border&#xff08;边框&#xff09;&#xff1a;盒子的边框padding&#xff0…

美国如果把根域名服务器封了,中国不会从网络上消失

目录 美国如果把根域名服务器封了&#xff0c;中国不会从网络上消失为什么根服务器最多13个 输入URL后发生了什么 参考 https://www.yuque.com/fcant/network/vhyvik#AuOqk 美国如果把根域名服务器封了&#xff0c;中国不会从网络上消失 来源&#xff1a; https://segmentfau…

TwinCAT3 Modbus-TCP Client/Server使用

目录 一、环境配置和准备 1、PLC中安装TF6250-Modbus-TCP库 2、勾选TF6250的license 3、PLC工程中添加Tc2_ModbusSrv库文件 4、分别创建测试ModbusTCP测试的Server和Client程序 二、PLC作为Client端 1、设置测试电脑IP地址 2、运行MobusTCP测试工具 3、PLC端程序编写 …

基于C/C++的rapidxml加载xml大文件 - 下部分

下载地址: RapidXml (sourceforge.net)https://rapidxml.sourceforge.net/ 将源码添加到自己的工程中 示例测试大文件耗时: 总共293w行数据&#xff0c;大概耗时不到1s。

将创建表字段语句快速转换成golang struct字段

用网页jquery快速生成 本地建立 struct.html <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>leo-转换</title> <script src"https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></s…