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

这篇具有很好参考价值的文章主要介绍了从零开始c++精讲:第三篇——内存管理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


一、C/C++内存分布

为什么要有内存区域的划分呢?
因为不同数据有不同的存储需求,各区域满足不同的需求。
从零开始c++精讲:第三篇——内存管理,C++快速入门,c++,开发语言,内存管理

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

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

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

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

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

实战演练
从零开始c++精讲:第三篇——内存管理,C++快速入门,c++,开发语言,内存管理

从零开始c++精讲:第三篇——内存管理,C++快速入门,c++,开发语言,内存管理
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修饰的,它是在常量区的
从零开始c++精讲:第三篇——内存管理,C++快速入门,c++,开发语言,内存管理

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

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

从零开始c++精讲:第三篇——内存管理,C++快速入门,c++,开发语言,内存管理

二、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;
}

从零开始c++精讲:第三篇——内存管理,C++快速入门,c++,开发语言,内存管理
ps:c++的new比起c语言的malloc的好处在哪里1?
malloc不方便动态申请自定义对象的初始化问题。
new更方便自定义类型的初始化
从零开始c++精讲:第三篇——内存管理,C++快速入门,c++,开发语言,内存管理

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;
}

从零开始c++精讲:第三篇——内存管理,C++快速入门,c++,开发语言,内存管理

再举个例子:

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会自动开空间,也会自动调构造和析构函数
从零开始c++精讲:第三篇——内存管理,C++快速入门,c++,开发语言,内存管理

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了一块数组。
从零开始c++精讲:第三篇——内存管理,C++快速入门,c++,开发语言,内存管理
而在调delete的时候,是先清理构造函数请求的那块空间,也就是_a指向的空间。然后再释放栈对象(也就是p1指向的空间)。否则你先把栈对象释放了,那栈对面里面的_a指向的空间就找不到了。

从零开始c++精讲:第三篇——内存管理,C++快速入门,c++,开发语言,内存管理
从零开始c++精讲:第三篇——内存管理,C++快速入门,c++,开发语言,内存管理

四、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;
}

从零开始c++精讲:第三篇——内存管理,C++快速入门,c++,开发语言,内存管理
operator new 和operator delete功能上和malloc和free功能上是一样的,但是如果开空间或者销毁空间失败之后,是抛异常的,这是C语言的malloc和free没有的。

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

从零开始c++精讲:第三篇——内存管理,C++快速入门,c++,开发语言,内存管理
后面调用delete,会先调用10次析构函数(会先让p3往前4个字节,找到要析构几次的值,这里是10),再往后就是调用operate delete[ ](ps:operate delete[ ]内部又会调用10次operate delete,然后是更底层的free函数)

从零开始c++精讲:第三篇——内存管理,C++快速入门,c++,开发语言,内存管理

五、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)(了解)

从零开始c++精讲:第三篇——内存管理,C++快速入门,c++,开发语言,内存管理

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的区别

从零开始c++精讲:第三篇——内存管理,C++快速入门,c++,开发语言,内存管理

7.2内存泄漏

从零开始c++精讲:第三篇——内存管理,C++快速入门,c++,开发语言,内存管理
从零开始c++精讲:第三篇——内存管理,C++快速入门,c++,开发语言,内存管理



文章来源地址https://www.toymoban.com/news/detail-810501.html

到了这里,关于从零开始c++精讲:第三篇——内存管理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++练级之初级:第三篇

    🤔首先我们先解决一下为什么C++支持函数重载,而C语言不支持? 这里就不得不提起编译链接了😁; 👉这是编译链接篇 以这三个简单的文件为例: 预处理阶段: 头文件的展开,条件编译,宏的替换,注释的删除等,最终处理完这些后test.c就会变成test.i,add.c就会变成add.i;

    2023年04月23日
    浏览(54)
  • 【jenkins】第三篇:jenkins凭据管理

    凭证管理 【系统管理】—【凭据管理】 1.1 创建Gitlab私人令牌 点击创建后,提示个人令牌,一定要先复制出来保存好,因为一刷新页面token就看不到了。 1.2 添加Gitlab API令牌凭据 【系统管理】—【凭据】—【全局】—【添加凭据】 选择 Gitlab API 令牌 类型,输入 Gitlab私人令牌

    2024年02月03日
    浏览(62)
  • 【Python微信机器人】第三篇:使用ctypes调用进程函数和读取内存结构体

    目前的系列目录(后面会根据实际情况变动): 在windows11上编译python 将python注入到其他进程并运行 注入Python并使用ctypes主动调用进程内的函数和读取内存结构体 使用汇编引擎调用进程内的任意函数 利用beaengine反汇编引擎的c接口写一个pyd库,用于实现inline hook 利用beaengine反汇编

    2024年02月06日
    浏览(41)
  • C++知识第三篇之继承

    继承是 面向对象编程 的重要特征,是对类设计层次的复用 1.继承定义 Parent :是父类,也称作基类(base class); Student :是子类,也称作派生类(derived class) public :表示继承方式为公共继承 2.继承方式 子类继承父类,有3种继承方式 继承方式的作用是: 对于父类非private的成员

    2024年02月06日
    浏览(66)
  • 【MySQL进阶之路丨第三篇】MySQL管理与连接

    在上一篇中我们介绍了 MySQL数据库的安装与配置,这一篇我们开始正式学习MySQL 上一篇链接: 【MySQL进阶之路丨第二篇】数据库的安装与配置 如何创建、管理和优化数据库,以及如何进行数据的存储、检索和更新等操作是十分重要的。这一篇我们要讲的是【MySQL管理与连接】

    2024年02月10日
    浏览(36)
  • C++ STL第三篇(搞清楚deque原理和有多少用法)

    Vector容器是单向开口的连续内存空间,deque则是一种双向开口的连续线性空间。所谓的双向开口,意思是可以在头尾两端分别做元素的插入和删除操作,当然,vector容器也可以在头尾两端插入元素,但是在其头部操作效率奇差,无法被接受。 Deque容器和vector容器最大的差异,

    2024年03月17日
    浏览(34)
  • 第三篇【传奇开心果系列】Vant开发移动应用:财务管理应用

    使用vant实现财务管理应用:创建一个简单的财务管理应用,用户可以记录和跟踪他们的收入和支出,并生成报表和图表展示财务状况。 1. 首先,安装并引入Vant组件库,以便使用Vant提供的丰富组件来构建财务管理应用界面。 创建一个首页,包括收入、支出、报表和图表四个

    2024年01月22日
    浏览(55)
  • 【从零开始学习JAVA | 第三十篇】方法引用

    目录 前言: 方法引用: 方法引用基本概念: 方法可以被引用的条件: 方法引用的种类: 方法引用的优点: 总结: 方法引用作为一个重要的知识点,虽然他使用起来很复杂,而且会降低代码的可读性,但是如果用好了方法引用,我们也会获得不错的效率,因此我们在今天

    2024年02月15日
    浏览(43)
  • 云原生(第三篇)-k8s资源管理的两种方式

    1.kubernetes 集群管理集群资源的唯一入口是通过相应的方法调用 apiserver 的接口 2.kubectl 是官方的CLI命令行工具,用于与 apiserver 进行通信,将用户在命令行输入的命令,组织并转化为 apiserver 能识别的信息,进而实现管理 k8s 各种资源的一种有效途径 3.kubectl 的命令大全 kubectl

    2024年02月13日
    浏览(58)
  • c++类开发的第三篇(讲明白友元函数和this指针)

    c++实现了 封装 , 数据 和 处理数据的操作(函数) 是分开存储的。 c++中的 非静态数据成员 直接内含在类对象中,就像c语言的struct一样。 成员函数并不会出现在对象中,而是作为类的一部分存储在代码段中,需要通过对象或对象指针进行调用。成员函数可以访问类的所有成员

    2024年02月21日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包