C++内存管理(动态内存开辟)

  我们在C语言当中想要使用堆区的空间的时候就需要使用malloc函数进行手动的申请,但是我们在申请的时候需要手动进行计算,经过计算之后还需要进行判空操作,并且还不能进行任意值的初始化。这一切看起来在学习完C++当中的动态开辟之前显得很正常,但是在学习完C++当中的动态内存开辟之后我们就会感绝倒C语言的麻烦之处,接下来我们就来认识一下C++当中的动态内存开辟操作符,并将它和C语言当中的malloc函数进行对比吧。

  一:认识new和delete操作符

  在C++当中想要动态内存开辟的时候我们使用的是new和delete操作符。我们通过一段代码来学习他的使用方法:

#include<iostream>
int main()
{
	//使用new操作符进行动态内存开辟,使用delete进行内存空间的释放
	//开辟一个字符的空间并使用
	char* pyes = new char;
	*pyes = 'y';
	std::cout << *pyes << std::endl;
	//开辟一个连续的空间
	int* pret = new int[3];
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		pret[i] = i + 1;
		std::cout << pret[i] <<" ";
	}
	//使用delete释放单个元素开辟的空间
	delete pyes;
	//使用delete释放动态开辟数组的空间的时候需要使用以下格式进行空间的释放
	delete[] pret;
	return 0;
}

  通过上面的代码我们肯定可以发现C++当中很多不同的用法。想要动态内存开辟就需要使用new操作符,new操作符后面跟的是我们想要开辟的空间的类型,不像C语言当中的malloc函数返回值是void所以在使用的时候还需要对目标的空间进行强转,new开辟出来的空间直接使用即可。

  如果想要开辟一串连续的空间的话,我们只需要在new的类型的后面加上一个方括号 [ ] 里面填入我们想要的元素的个数即可。不再需要我们自己亲手计算。在申请完空间之后我们不再需要对其进行判空操作,直接使用即可。因为我们的new操作符在申请失败的时候会抛出一个异常我们以后可以通过捕获进行获得异常的报错。(捕获等后面我们再详细讲解)这也就避免了我们重复判断的麻烦。使用的方式和正常的相同。

  在使用完毕的时候我们使用delete将我们开辟的空间释放即可。对于单个空间我们只需要使用delete加上指针即可,对于连续的空间我们使用delete [ ] 指针进行释放。如上程序运行的结果如下:

   但是我们在上面说到过new和malloc的区别之一就是可以对申请的空间进行任意的赋值,这和我们的malloc甚至是calloc函数都有很大的区别。同样的,我们通过一段代码进行详细的了解:

#include<iostream>
int main()
{
	//尝试使用new开辟空间并初始化
	//开辟单个字符的空间并初始化
	int* pret = new int(3);
	std::cout << *pret << std::endl;
	delete pret;
	//开辟一个数组的空间并对其进行初始化
	int* ptmp = new int[2]{3,4};
	std::cout << ptmp[0] << " " << ptmp[1] << std::endl;
	delete[] ptmp;
	//开辟一个数组的空间,使用默认的初始化方式进行试验
	int* pres = new int[3]();
	std::cout << pres[0] << " " << pres[1] << " " << pres[2] << std::endl;
	delete[] pres;
	return 0;
}

  当我们想要对单个字符进行初始化的时候我们只需要在类型的名称后面加上一个小括号,里面输入我们想要初始化的值即可。但是对于一个数组来说我们所需要进行的操作就发生了些许的改变:我们需要分为很多种情况:1.具有构造函数的情况 2.没有构造函数的情况 我们来一一对其进行解释。

  1.如果没有构造函数就会调用系统的默认初始化的方式,后面可以加上括号但是什么值都不能有,系统会将数组的值默认初始化为0。如果想要对没有构造函数的数组进行初始化我们还可以通过大括号 { } 的当时一一对数组的元素进行赋值,但是并不能统一进行赋值。如上代码运行的结果:

  2.对具有默认构造的对象进行初始化,我们对于具有默认构造的函数进行初始化的情况,大多数都是针对于类来说的,我们通常会在类当中写一个构造函数,之后我们在使用new操作符开辟空间的时候就会自动调用类的构造函数进行对象的初始化。同样的,我们通过一段代码来学习具有构造函数的对象如何进行初始化:

#include<iostream>

//写一个类,在使用new开辟空间的时候验证默认会自动调用默认初始化
class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	//写一个构造函数,用于验证初始化
	Date(int year = 2022, int month = 12, int day = 12)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void print()
	{
		std::cout << _year << "年" << _month << "月" << _day <<"日"<< std::endl;
	}
};
int main()
{
	//使用new开辟一个类大小的空间,并验证其初始化的内容
	Date* pret = new Date;
	(*pret).print();
	//开辟多个类的空间,并验证其初始化的内容
	Date* ptmp = new Date[3];
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		ptmp[i].print();
	}
    delete pret;
    delete[] ptmp;
	return 0;
}

  经过上面的代码我们可以看到,在使用new进行默认初始化的时候,当存在默认构造函数的时候就会自动调用默认构造函数进行初始化,如果像针对某个对象进行特定初始化的时候我们同样可以使用 { } 进行指定的初始化。 程序运行的结果如下:

  二:new和delete操作符的原理 

  在认识了new和delete函数的使用之后相信大家对于这两个操作符可以实现该功能的原因,所以我们就在此进行进一步的原理讲解。

  就像我们上面所看到的现象所展示的那样,假如我们在申请堆区的指定的空间的时候,对于有默认构造的对象会调用指定的默认构造,对于没有默认构造的对象想要初始化要么将其初始化为0,要么就需要对其进行指定的构造。所以我们也可以联想到我们的new操作符是不是调用了我们类当中的构造函数?我们对其进行验证:

  我们可以通过上面的运行结果得到答案,可以看出我们的程序是一定调用了构造函数了的。

  与之相对应的我们的delete操作符在发挥作用的时候也可以验证一下是否调用了析构函数:

   同样我们可以知道的是,在使用delete操作符的时候调用了我们类当中所定义的析构函数。

  实质上C++当中的操作符在定义的时候其实没有那么高级,也并不是说有了new就可以完全舍弃malloc函数。在底层的封装之上其实new操作符就是使用我们的malloc函数再加上我们的构造函数所得到的。本质上我们的new操作符在发挥作用的时候会先调用malloc函数进行空间的申请,假如申请成功之后再调用我们类的构造函数,这样就形成的new操作符。delete操作符的原理其实是一样的。我们在空间释放的时候会先调用析构函数,在将我们的数据清理了之后再调用free函数将我们申请的空间释放掉。

三:定向空间申请

  这其实并不是new操作符的使用方法当中最巧妙的一个,最巧妙的是:当我们需要多次向堆区申请空间的时候,每一次的申请都会造成一定程度上的浪费。所以我们可以现申请好一整块的空间,之后使用我们申请好的空间即可。这样就可以免去我们连续调用malloc函数所带来的缺陷。我们同样通过一段代码进行学习这种使用的方法:

#include<iostream>

//写一个类,在使用new开辟空间的时候验证默认会自动调用默认初始化
class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	//写一个构造函数,用于验证初始化
	Date(int year = 2022, int month = 12, int day = 12)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void print()
	{
		std::cout << _year << "年" << _month << "月" << _day << "日" << std::endl;
	}
};
int main()
{
	//使用new开辟一个类大小的空间,并验证其初始化的内容
	Date* pret = new Date[3];
	pret[0] = {2023,1,1};
	pret[0].print();
	return 0;
}

  其使用的方法为:new (place_address) type或者new (place_address) type(initializer-list)

  其中place_address必须是一个指针,initializer-list是类型的初始化列表。也就是说我们new后面括号里的是我们提前申请好的地址的指针,括号后面的 type 表示我们使用的地址的类型。我们还可以在后面的 { } 里面进行初始化,但是我们需要注意的是:我们进行的初始化只能是单个对象的初始化,否则就有可能产生特殊意想不到的结果。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Pytorch个人学习记录总结 10

目录 优化器 优化器 官方文档地址&#xff1a;torch.optimhttps://pytorch.org/docs/stable/optim.html Debug过程中查看的grad所在的位置&#xff1a; model --> Protected Atributes --> _modules --> ‘model’ --> Protected Atributes --> _modules -…

【PostgreSQL】系列之 一 CentOS 7安装PGSQL15版本(一)

目录 一、何为PostgreSQL&#xff1f; 二、PostgreSQL安装 2.1安装依赖 2.2 执行安装 2.3 数据库初始化 2.4 配置环境变量 2.5 创建数据库 2.6 配置远程 2.7 测试远程 三、常用命令 四、用户创建和数据库权限 一、何为PostgreSQL&#xff1f; PostgreSQL是以加州大学…

MacOS Monterey VM Install ESXi to 7 U2

一、MacOS Monterey ISO 准备 1.1 下载macOS Monterey 下载&#x1f517;链接 一定是 ISO 格式的&#xff0c;其他格式不适用&#xff1a; https://www.mediafire.com/file/4fcx0aeoehmbnmp/macOSMontereybyTechrechard.com.iso/file 1.2 将 Monterey ISO 文件上传到数据…

shell清理redis模糊匹配的多个key

#!/bin/bash# 定义Redis服务器地址和端口 REDIS_HOST"localhost" REDIS_PORT6380# 获取匹配键的数量 function get_matching_keys() {local key_pattern"$1"redis-cli -h $REDIS_HOST -p $REDIS_PORT -n 0 KEYS "$key_pattern" }# 删除匹配的键 …

java+springboot+mysql企业邮件管理系统

项目介绍&#xff1a; 使用javaspringbootmysql开发的企业邮件管理系统&#xff0c;系统包含超级管理员、管理员、员工角色&#xff0c;功能如下&#xff1a; 超级管理员&#xff1a;管理员管理&#xff1b;员工管理&#xff1b;反馈管理&#xff1b;系统公告&#xff1b;个人…

【Linux学习】日积月累——进程控制

一、进程创建 1.1 fork函数的认识 #include<unistd.h> pid_t fork(void); 返回值&#xff1a;自进程返回0&#xff0c;父进程返回子进程PID&#xff0c;出错返回-1进程调用fork&#xff0c;当控制转移到内核中的fork代码后&#xff0c;内核做&#xff1a;分配新的内存块…

Linux ALSA音频工具aplay、arecord、amixer的使用方法

ALSA 是Advanced Linux Sound Architecture的缩写&#xff0c;先进的Linux音频架构&#xff0c;为Linux操作系统提供音频和MIDI功能。 aplay命令 aplay是播放命令。 rootimx6ul7d:~# aplay -h Usage: aplay [OPTION]... [FILE]...-h, --help help--version …

解决Hadoop审计日志hdfs-audit.log过大的问题

【背景】 新搭建的Hadoop环境没怎么用&#xff0c;就一个环境天天空跑&#xff0c;结果今天运维告诉我说有一台服务器磁盘超过80%了&#xff0c;真是太奇怪了&#xff0c;平台上就跑了几个spark测试程序&#xff0c;哪来的数据呢&#xff1f; 【问题调查】 既然是磁盘写满了&…

浅谈微服务异步解决方案

导言 异步是一种设计思想&#xff0c;不是设计目的&#xff0c;因此不要为了异步而异步&#xff0c;要有所为&#xff0c;有所不为。 异步不是『银弹』&#xff0c; 避免试图套用一个『异步框架』解决所有问题&#xff0c; 需要根据不同的业务特点或要求&#xff0c;选择合适的…

VSCode自定义闪烁光标

打开VSCode 组合键ctrlshiftp搜索"settings.json",打开User Settings 加上这一句 "editor.cursorStyle": "block","workbench.colorCustomizations": {"editorCursor.foreground": "#5c8fb1","terminalCurs…

IDEA中Git面板操作介绍 变基、合并、提取、拉取、签出

IDEA中Git面板操作介绍 变基、合并、提取、拉取、签出 面板介绍 变基、合并 提取、拉取 签出、Checkout 面板介绍 如图&#xff0c;在IDEA的Git面板中&#xff0c;仓库会分为本地仓库和远程仓库&#xff0c;代码仓库里面放的是各个分支。 分支前面的书签&#x1f516;标志…

JavaScript高级——ES6基础入门

目录 前言let 和 const块级作用域模板字符串一.模板字符串是什么二.模板字符串的注意事项三. 模板字符串的应用 箭头函数一.箭头函数是什么二.普通函数与箭头函数的转换三.this指向1. 全局作用域中的 this 指向2. 一般函数&#xff08;非箭头函数&#xff09;中的this指向3.箭头…

MongoDB文档--基本安装-linux安装(mongodb环境搭建)-docker安装(挂载数据卷)-以及详细版本对比

阿丹&#xff1a; 前面了解了mongodb的一些基本概念。本节文章对安装mongodb进行讲解以及汇总。 官网教程如下&#xff1a; 安装 MongoDB - MongoDB-CN-Manual 版本特性 下面是各个版本的选择请在安装以及选择版本的时候参考一下&#xff1a; MongoDB 2.x 版本&#xff1a…

ELK日志分析系统概述及部署

ELK 平台是一套完整的日志集中处理解决方案&#xff0c;将 ElasticSearch、Logstash 和 Kibana 三个开源工具配合使用&#xff0c;完成更强大的用户对日志的查询、排序、统计需求。 一、ELK概述 1、组件说明 ①ElasticSearch ElasticSearch是基于Lucene&#xff08;一个全文…

XML约束和解析

文章目录 概述使用场景语法dtd约束Schema约束解析DOM4j&#xff08;重点&#xff09; 概述 可扩展的标记性语言 使用场景 以前: 传输数据的媒介。 例如&#xff1a;微服务架构中&#xff0c;可以用xml文件进行多语言之间的的联系。 现在: 做配置文件 现在作为传输数据的媒介…

使用Gunicorn+Nginx部署Flask项目

部署-开发机上的准备工作 确认项目没有bug。用pip freeze > requirements.txt将当前环境的包导出到requirements.txt文件中&#xff0c;方便部署的时候安装。将项目上传到服务器上的/srv目录下。这里以git为例。使用git比其他上传方式&#xff08;比如使用pycharm&#xff…

Matlab Optimization Toolbox中的遗传算法工具包(GA)

matlab optimization 中使用了GA求解器 默认的是小于等于 找到GA 工具包 找到 APP选择 Optimization Tool 选择Solver ga - Genetic Algorithm 应用GA solver 定义适应度函数(Fitness function)与问题约束(Constraints) example one 优化函数 sin(x) 2 * cos(x)极其重要的…

浅谈3D隐式表示(SDF,Occupancy field,NeRF)

本篇文章介绍了符号距离函数Signed Distance Funciton(SDF)&#xff0c;占用场Occupancy Field&#xff0c;神经辐射场Neural Radiance Field&#xff08;NeRF&#xff09;的概念、联系与区别。 显式表示与隐式表示 三维空间的表示形式可以分为显式和隐式。 比较常用的显式表…

高并发与性能优化的神奇之旅

作为公司的架构师或者程序员&#xff0c;你是否曾经为公司的系统在面对高并发和性能瓶颈时感到手足无措或者焦头烂额呢&#xff1f;笔者在出道那会为此是吃尽了苦头的&#xff0c;不过也得感谢这段苦&#xff0c;让笔者从头到尾去探索&#xff0c;找寻解决之法。 目录 第一站…

让数据管理由繁至简的低代码开发平台

随着社会数字化能力的快速升级&#xff0c;各行各业正逐渐迈向数字化转型的新时代。尤其是AI的爆发&#xff0c;数据智能技术正在彻底改变着这个行业的面貌&#xff0c;随着越来越多的企业开始将人工智能、机器学习和大数据分析技术应用到其业务中&#xff0c;数据的价值正在得…