从零开始c++精讲:第三篇——内存管理

news2024/11/18 14:26:09

文章目录

  • 一、C/C++内存分布
  • 二、C语言中动态内存管理方式:malloc/calloc/realloc/free
  • 三、C++中动态内存管理
  • 四、operator new与operator delete函数
    • 4.1 operator new与operator delete函数(重点)
  • 五、new和delete的实现原理
    • 5.1内置类型
    • 5.2 自定义类型
  • 六、定位new表达式(placement-new)(了解)
  • 七、常见面试题
    • 7.1 malloc/free和new/delete的区别
    • 7.2内存泄漏


一、C/C++内存分布

为什么要有内存区域的划分呢?
因为不同数据有不同的存储需求,各区域满足不同的需求。
在这里插入图片描述

栈(堆栈):一般存放临时用的,比如非静态局部变量/函数参数/返回值等,栈是向下增长的。

:有动态使用的需求,需要的时候你给我,不需要的时候你释放。也就是出了函数作用域,这个变量不会自动销毁,只有我不需要它了才销毁。

数据段(静态区):需要长期使用/整个运行期间都使用的。比如全局数据或静态数据。

代码段(常量区):只读数据不修改。比如可执行代码/只读常量。

内存映射段:是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。(Linux课程如果没学到这块,现在只需要了解一下)

实战演练
在这里插入图片描述

在这里插入图片描述
globalval:是全局变量,在静态区

staticGlobalVal:被static修饰的全局变量,还是在静态区
ps:globalval和staticGlobalVal的区别是在链接属性,globalval可在各个文件可用,staticGlobalVal只在当前文件可用

staticVal:被static修饰的局部变量,还是在静态区
ps:staticGlobalVal可在当前文件用,staticVal只能在所处函数中用。
但是对于生命周期而言,globalval和staticGlobalVal和staticVal是一样的

localVar:一个简单的临时变量,存储在栈上,没啥好说的

num1:虽然是个数组但也是一个临时变量,存储在栈上

char2:这个和num1类似的,都是数组,只不过类型变了。还是存储在栈上

*char2:相当于把指针指向内容解引用,数组上的元素还是在栈内的。

pchar3:是一个指针,定义在栈上。这里的const修饰的是指针指向的内容,不是修饰的指针。

*pchar3:是指针指向的内容,而指针指向的内容是被const修饰的,它是在常量区的
在这里插入图片描述

ptr1:本身是一个栈上的变量,它指向一个堆上的空间

*ptr1:是指针的解引用,指针是指向堆的,解引用就是堆那片空间。

在这里插入图片描述

二、C语言中动态内存管理方式:malloc/calloc/realloc/free

void Test ()
{
int* p1 = (int*) malloc(sizeof(int));
free(p1);
// 1.malloc/calloc/realloc的区别是什么?
int* p2 = (int*)calloc(4, sizeof (int));
int* p3 = (int*)realloc(p2, sizeof(int)*10);
// 这里需要free(p2)吗?
free(p3 );
}

三、C++中动态内存管理

C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理

int main()
{
	int* p1 = new int;// 动态申请一个int类型的空间

	int* p2 = new int[10];//申请10个空间
	
	//ps:内置类型默认不初始化,只是单纯开空间

	//如果想初始化,则单个数据加圆括号,多个数据加花括号,括号里面附上初始化内容
	int* p3 = new int(1);
	int* p4 = new int[3]{1,2,3};
	int* p5 = new int[10]{ 1,2,3 };//初始化的数量不够整个数组大小,则剩下的默认是0

	delete p1;
	delete[] p2;
	delete p3;
	delete[] p4;
	delete[] p5;

	return 0;
}

在这里插入图片描述
ps:c++的new比起c语言的malloc的好处在哪里1?
malloc不方便动态申请自定义对象的初始化问题。
new更方便自定义类型的初始化
在这里插入图片描述

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

int main()
{
	//malloc不方便动态申请自定义对象的初始化问题
	A* p1 = new A;//自定义类型,不给参数调用默认构造函数
	A* p2 = new A(1);//给了参数,调用所给参数的构造函数

	//初始化多个对象
	//法一:
	A aa1(1);
	A aa2(2);
	A aa3(3);
	A* p3 = new A[3]{aa1,aa2,aa3};

	//法二:匿名对象的方法
	A* p4 = new A[3]{ A(1),A(2),A(3) };

	//法三:让编译器自己去调构造函数
	A* p5 = new A[3]{ 1,2,3 };

	delete p1;//delete会调用析构函数+释放空间
	delete p2;
	delete[] p3;
	delete[] p4;
	delete[] p5;
	return 0;
}

在这里插入图片描述

再举个例子:

struct ListNode
{
	int _val;
	ListNode* _next;

	ListNode(int val)
		:_val(val)
		, _next(nullptr)
	{}
};

int main()
{
	//相比c语言,c++就不用再写什么createNode函数了,你之间用new开空间就行
	ListNode* n1 = new ListNode(1);
	ListNode* n1 = new ListNode(2);
	ListNode* n1 = new ListNode(3);
}

ps:内置类型的对象申请释放,new和malloc除了用法,其他没有区别。但是自定义类型,new可以自己初始化。

对于自定义类型,new和delete会自动开空间,也会自动调构造和析构函数
在这里插入图片描述

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

	Stack *p1 = new Stack;//也可以直接开辟一个栈的大小(指针4字节,加两个4字节的int)
	return 0;
}

你也可以直接new一个Stack大小的空间,然后用p1指向这块空间,内存中情况如下:
p1指向这个对象大小的空间,然后构造函数里的a又new了一块数组。
在这里插入图片描述
而在调delete的时候,是先清理构造函数请求的那块空间,也就是_a指向的空间。然后再释放栈对象(也就是p1指向的空间)。否则你先把栈对象释放了,那栈对面里面的_a指向的空间就找不到了。

在这里插入图片描述
在这里插入图片描述

四、operator new与operator delete函数

4.1 operator new与operator delete函数(重点)

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

operator new 和operator delete库里的全局函数,封装了malloc和free

class Stack
{
public:
	Stack(int capacity = 4)
	{
		cout << "Stack(int capacity=4)" << endl;
		_a = new int[capacity];
		_top = 0;
		_capacity = capacity;
	}
	~Stack()
	{
		cout << "~Stack()" << endl;
		delete[] _a;
		_a = nullptr;
		_top = 0;
		_capacity = 0;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
int main()
{
	//Stack *p1 = new Stack;//也可以直接开辟一个栈的大小(指针4字节,加两个4字节的int)
	//delete p1;

	Stack* p2 = (Stack*)operator new(sizeof(Stack));
	operator delete(p2);
	//p2这种用法功能是和C语言的malloc和free是一样的
	//operator new不会调用构造,operator delete也不会调用析构
	return 0;
}

在这里插入图片描述
operator new 和operator delete功能上和malloc和free功能上是一样的,但是如果开空间或者销毁空间失败之后,是抛异常的,这是C语言的malloc和free没有的。

另外,如果你是开一个自定义类型的数组,它会自动给你开辟一个sizeof(自定义类型)*数量的空间大小

在这里插入图片描述
后面调用delete,会先调用10次析构函数(会先让p3往前4个字节,找到要析构几次的值,这里是10),再往后就是调用operate delete[ ](ps:operate delete[ ]内部又会调用10次operate delete,然后是更底层的free函数)

在这里插入图片描述

五、new和delete的实现原理

5.1内置类型

如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:
new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申
请空间失败时会抛异常,malloc会返回NULL。

5.2 自定义类型

new的原理

  1. 调用operator new函数申请空间
  2. 在申请的空间上执行构造函数,完成对象的构造

delete的原理

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

new T[N]的原理

  1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对
    象空间的申请
  2. 在申请的空间上执行N次构造函数

delete[]的原理

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

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

在这里插入图片描述

class A
{
public:
	A(int a = 0)
		: _a(a)
	{
		cout << "A():" << this << endl;
	}
	~A()
	{
		cout << "~A():" << this << endl;
	}
private:
	int _a;
};
// 定位new/replacement new
int main()
{
	// p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行
	A* p1 = (A*)malloc(sizeof(A));
	new(p1)A; // 注意:如果A类的构造函数有参数时,此处需要传参
	p1->~A();
	free(p1);
	A* p2 = (A*)operator new(sizeof(A));
	new(p2)A(10);
	p2->~A();
	operator delete(p2);
	return 0;
}

七、常见面试题

7.1 malloc/free和new/delete的区别

在这里插入图片描述

7.2内存泄漏

在这里插入图片描述
在这里插入图片描述



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

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

相关文章

C++总结笔记

1. 简介 1、面向对象程序设计 面向对象的四大特性 1&#xff09;封装 2&#xff09;继承 3&#xff09;多态 4&#xff09;抽象 2、标准库 标准C由三个部分组成 1&#xff09;核心语言&#xff1a;提供了所有的构件块 2&#xff09;C标准库&#xff1a;提供了大量的函…

web蓝桥杯真题--11、蓝桥知识网

介绍 蓝桥为了帮助大家学习&#xff0c;开发了一个知识汇总网站&#xff0c;现在想设计一个简单美观的首页。本题请根据要求来完成一个首页布局。 准备 开始答题前&#xff0c;需要先打开本题的项目代码文件夹&#xff0c;目录结构如下&#xff1a; ├── css │ └──…

浅谈ARP协议

ARP是 address resolution protocol的缩写&#xff0c;意思是地址解析协议&#xff0c;处于OSI七层模型的网络层&#xff0c;它的作用是根据Ip地址找到mac地址&#xff0c;实际上我们需要访问某一个ip时&#xff0c;是要先找到它的mac地址&#xff0c;也就是物理地址才行的&…

Windows系统下使用docker-compose安装mysql8和mysql5.7

windows环境搭建专栏&#x1f517;点击跳转 win系统环境搭建&#xff08;十四&#xff09;——Windows系统下使用docker安装mysql8和mysql5.7 文章目录 win系统环境搭建&#xff08;十四&#xff09;——Windows系统下使用docker安装mysql8和mysql5.7MySQL81.新建文件夹2.创建…

C++播放音乐:使用EGE图形库

——开胃菜&#xff0c;闲话篓子一大片 最近&#xff0c;我发现ege图形库不是个正经的图形库—— 那天&#xff0c;我又在打趣儿地翻代码时&#xff0c;无意间看到了这个&#xff1a; 图形库&#xff1f;&#xff01;你哪来的音乐&#xff08;Music&#xff09;呢&#xff1f…

【蓝桥备赛】求阶乘

题目链接 求阶乘 个人想法 之前做过计算阶乘结果后面有几个0的题目&#xff0c;这里看到本题之后&#xff0c;很快就有思路了。想要得到阶乘结果有几个0&#xff0c;首先尾数后面的0&#xff0c;最小肯定是因为因子中存在10。然后&#xff0c;10如何得来呢&#xff1f; 2 * …

【LeetCode】141. 环形链表

leetcode题目链接 141. 环形链表 #include <stdio.h> #include <stdbool.h>struct ListNode {int val;struct ListNode* next; }; typedef struct ListNode ListNode;bool hasCycle(ListNode* head) {ListNode* slow head, * fast head;while (fast &&…

SpringBoot 3.1.7 集成Mybatis

一、介绍 Mybatis的中文官网并没找到与SpringBoot最新的集成的教程&#xff0c;有的都是老式的配置方法&#xff0c;所以记录一下怎么我是怎么集成SpringBoot 3.1.7 集成Mybatis 的方法 有条件的可以打开源网站 https://github.com/mybatis/spring-boot-starter 没有条件的我…

一款满足基层医疗机构各类业务需要的:健康云HIS系统源码,功能包括病患问诊、电子病历、开药发药、住院管理、护理文书、病案管理等功能。

一款满足基层医疗机构各类业务需要的健康云HIS系统。该系统能帮助基层医疗机构完成日常各类业务&#xff0c;提供病患挂号支持、病患问诊、电子病历、开药发药、会员管理、护理文书、病案管理、统计查询、医生站和护士站等一系列常规功能&#xff0c;能与公卫、PACS等各类外部系…

C++-类和对象(3)

1. 再谈构造函数 1.1 构造函数体赋值 我们在创建一个对象时&#xff0c;编译器会调用该对象的构造函数对该对象的成员进行初始化。 class Date { public:Date(int year, int month, int day){_year year;_month month;_day day;} private:int _year;int _month;int _day…

通过代理如何调通openai的api

调通openai的api 一、前提二、通过curl调通openai的api三、通过python调通openai的api 一、前提 会魔法上网本地运行代理软件&#xff0c;知道端口号&#xff08;如1081&#xff09;。 127.0.0.1:1081二、通过curl调通openai的api 如果在国外&#xff0c;没有qiang&#xff…

AWS 专题学习 P7 (FSx、SQS、SNS)

文章目录 Amazon FSx – 概述Amazon FSx for LustreFSx Lustre - 文件系统部署选项 Amazon FSx for NetApp ONTAPAmazon FSx for OpenZFSHybrid Cloud 存储AWS 存储云原生选项AWS 存储网关Amazon S3 File GatewayAmazon FSx File GatewayVolume GatewayTape GatewayStorage Gat…

设计一个Key-Value缓存去存储最近的Web Server查询的结果

1: 定义Use Case和约束 Use Cases 我们可以定义如下 Scope: User 发送一个 search request, 缓存命中成功返回DataUser 发送一个 search request, 缓存未命中&#xff0c;未成功返回DataService 有高可用 约束和假设 状态假设 Traffic 分布不是均匀的 热度高的查询总是被…

HarmonyOS鸿蒙学习基础篇 - 什么是HarmonyOS

概述 HarmonyOS是华为开发的一款面向未来的全场景分布式智慧操作系统&#xff0c;将逐步覆盖18N全场景终端设备&#xff1b; 对消费者而言 HarmonyOS用一个‘统一的软件系统’ 从根本上解决消费者面对大量智能终端体验割裂的问题&#xff0c;为消费者带来同意便利安全的智慧化全…

使用 Python 创造你自己的计算机游戏(游戏编程快速上手)第四版:第十五章到第十八章

十五、反转棋游戏 原文&#xff1a;inventwithpython.com/invent4thed/chapter15.html 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 在本章中&#xff0c;我们将制作反转棋&#xff0c;也称为黑白棋或奥赛罗。这个双人棋盘游戏是在网格上进行的&#xff0c;因此我们…

【Qt5】QString的成员函数trimmed

2024年1月19日&#xff0c;周五下午 QString 的 trimmed 方法是用于移除字符串两端的空白字符&#xff08;空格、制表符、换行符等&#xff09;的方法。它返回一个新的字符串&#xff0c;该字符串是原始字符串去除两端空白后的结果。 下面是一个简单的示例&#xff1a; #incl…

【Linux 内核源码分析】堆内存管理

堆 堆是一种动态分配内存的数据结构&#xff0c;用于存储和管理动态分配的对象。它是一块连续的内存空间&#xff0c;用于存储程序运行时动态申请的内存。 堆可以被看作是一个由各个内存块组成的堆栈&#xff0c;其中每个内存块都有一个地址指针&#xff0c;指向下一个内存块…

实体类(VO,DO,DTO)的划分

实体类&#xff08;VO&#xff0c;DO&#xff0c;DTO&#xff09;的划分 什么是“实体类” 实体类的主要职责是存储和管理系统内部的信息&#xff0c;它也可以有行为&#xff0c;甚至很复杂的行为&#xff0c;但这些行为必须与它所代表的实体对象密切相关。实体类有两方面内容…

51单片机8*8点阵屏

8*8点阵屏 8*8点阵屏是一种LED显示屏&#xff0c;它由8行和8列的LED灯组成。每个LED灯的开闭状态都可以独立控制&#xff0c;从而可以显示出数字、字母、符号、图形等信息。 8*8点阵屏的原理是通过行列扫描的方式&#xff0c;控制LED灯的亮灭&#xff0c;从而显示出所需的图案或…

使用MySQL建立外键约束时,报错3780的问题分析,和解决办法

今天在用语句给两个表建立外键约束时&#xff0c;报了3780的错误–具体描述如下&#xff1a; 大概意思就是或说&#xff0c;主表和从表的create_use 和 user_id 两个字段这不兼容 经过一顿分析之后发现&#xff0c;是因为这两个表的这两列数据类型不一样 解决办法–修改表中…