C++图文并茂分析讲解内存管理

 更新时间:2022年09月15日 15:48:55   作者:龟龟不断向前  
本章主要介绍C语言与C++的内存管理,以C++的内存分布作为引入,介绍C++不同于C语言的内存管理方式(new delete对比 malloc free),感兴趣的朋友来看看吧

1.了解一些基本的内存段(图演示)

验证栈是向下生长的

#include<iostream>
using namespace std;
int main()
{
	int a = 3;
	int b = 4;
	int c = 5;
	int d = 6;
	cout <<"a:"<< &a << endl;
	cout << "b:"<<&b << endl;
	cout << "c:"<<&c << endl;
	cout << "d:"<<&d << endl;
	return 0;
}

验证堆一般是向上生长的(不一定)

#include<iostream>
using namespace std;
int main()
{
	int num = 10;
	while (num--)
	{
		int *p1 = (int*)malloc(sizeof(int));
		int *p2 = (int*)malloc(sizeof(int));
		cout <<"p1"<< p1 << endl;
		cout <<"p2"<<p2 << endl;
		cout << endl;
		free(p1);
	}
	return 0;
}

一般情况下,p1的地址是比p2的地址高的(因为堆一般是向上生长的),但是有时候是不一定的。

巩固内存管理知识点

答案

温馨提示:题目中的指针是局部指针变量,是在栈上的,但是它指向的内容(解引用)可能是堆区或者常量区的。,可以画画图理解理解

2.c++申请动态内存的新玩儿法new,delete

回顾c语言动态内存管理的方式

malloccallocrealloc

  • malloc堆上动态开空间
  • calloc堆上动态开空间+初始化成0等价于malloc+memset
  • realloc指针已有的空间扩容

原题增容–后面又足够的空间

异地增容–后面没有足够的空间

开辟内置类型的空间

//C++开辟动态内存的新玩法
//语法演示:
#include<iostream>
using namespace std;
int main()
{
	//申请一个int的动态空间
	int* p1 = (int*)malloc(sizeof(int));
	*p1 = 1;
	int* p2 = new int(2);//这里是初始化
	free(p1);
	delete p2;
	//申请一个10各int的动态数组
	int *p3 = (int*)malloc(sizeof(int)* 10);
	for (int i = 0; i < 10; i++)
	{
		p3[i] = i + 1;
	}
	int *p4 = new int[10]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};//初始化
	free(p3);
	delete[]p4;
	return 0;
}

跟c语言的版本相比,c++的版本,初始化时显得比较方便,而且语法比较简洁。当然了,更大的优越性还在后面。

开辟自定义类型的空间(请用vs2013以上版本测试代码)

#include<iostream>
using namespace std;
struct ListNode
{
	int _val;
	ListNode* _next;
	ListNode* _prev;
	//构造函数
	ListNode(int val = 0)
	:_val(val)
	, _next(nullptr)
	, _prev(nullptr)
	{
        cout<<"ListNode(int val = 0)"<<endl;
    }
	~ListNode()
	{
		cout << "ListNode()" << endl;
	}
};
int main()
{
	//申请一个结点的空间
    ListNode* pa = (ListNode*)malloc(sizeof(ListNode)*4);
	ListNode* pb = new ListNode(1);//不用去用sizeof去计算空间大小,很方便   
	free(pa);
	delete pb;
	//申请4个结点的空间--当然了一般不会这么玩儿,我们只是看看效果
	ListNode* pc = (ListNode*)malloc(sizeof(ListNode)* 4);
	ListNode* pd = new ListNode[4]{1, 2, 3, 4};
	free(pc);
	delete[]pd;
	return 0;
}

​ 学过c语言版本的数据结构的小伙伴都知道,我们push_back一个结点时,需要先实现一个buynewnode的函数(创建一个结点,并且对其进行初始化)。而new这个操作符,在创建结点的同时,已经帮我们实现了结点的初始化。调用了构造函数,而且delete还调用了析构函数。

总结一下

  1. c++中,如果是申请内置类型对象或者数组,mallocnew没有太大区别
  2. 如果时自定义类型,区别很大,new和delete时开空间+初始化,析构清理+释放空间,mallocfree仅仅时开空间+释放空间
  3. 建议在c++中,无论时内置类型还是自定义类型的申请释放,尽量使用new和delete。

3. 32位平台下可以开辟多大的内存

我:cpu过来遭罪

cpu:你不要过来啊

上述程序,我们开了1.8 G左右的内存,加上需要堆上的小内存,最后的综合有2 G的空间

如何开辟4G的内存

将项目属性修改一下,改成64位平台即可,64位平台有2^34 G的空间(虚拟内存)在这我们不做细说,因为我也不太了解Linux。

4.了解operator new和operator delete

new其实是operator new + 构造函数

delete其实是operator delete+构造函数

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

大家可以将operator new和operator delete理解成和malloc 和free一样用法的函数。

唯一不同的地方是,operator new和malloc开辟空间失败的处理方式不一样,malloc是返回NULL空指针,而operator是抛异常,下面代码让大家看看效果,不必深究,以后会有介绍。

struct ListNode
{
	int _val;
	ListNode* _next;
	ListNode* _prev;
	//构造函数
	ListNode(int val = 0)
		:_val(val)
		, _next(nullptr)
		, _prev(nullptr)
	{
		cout << "ListNode(int val = 0)" << endl;
	}
	~ListNode()
	{
		cout << "ListNode()" << endl;
	}
};
void f()
{
	// 他的用法跟malloc和free是完全一样的,功能都是在堆上申请释放空间
	// 失败了处理方式不一样,malloc失败返回NULL,operator new失败以后抛异常
	ListNode* p1 = (ListNode*)malloc(sizeof(ListNode));
	free(p1);
	ListNode* p2 = (ListNode*)operator new(sizeof(ListNode));
	operator delete(p2);
	void* p3 = malloc(0x7fffffff);
	if (p3 == NULL)
	{
		cout << "malloc fail" << endl;
	}
	void* p4 = operator new(0x7fffffff);
	ListNode* p5 = new ListNode(2);
	cout << "继续" << endl;
}
//
//
int main()
{
	try
	{
		f();
	}
	catch (exception& e)
	{
		cout << e.what() << endl;
	}
	return 0;
}

malloc失败返回NULL,程序还会继续向下执行,但是operator new失败,就会报异常,f函数后面的没有在继续执行,然后走进catch语句中。

5.operator new和operator delete的类函数重载

struct ListNode//目的是提高效率
{
	ListNode* _next;
	ListNode* _prev;
	int _val;
	// 类中重载专属operator new
	void* operator new(size_t n)
	{
	void* p = nullptr;
	p = allocator<ListNode>().allocate(1);
	cout << "memory pool allocate" << endl;
	return p;
	}
	void operator delete(void* p)
	{
		allocator<ListNode>().deallocate((ListNode*)p, 1);
		cout << "memory pool deallocate" << endl;
	}
	ListNode(int val)
		:_next(nullptr)
		, _prev(nullptr)
		, _val(val)
	{}
};
int main()
{
	ListNode* p = new ListNode(1);
	delete p;
	return 0;
}

如果你自己在类里面写了operator newoperator delete,那么编译器就不会去调用系统提供的了,这是一种提高效率的方式。

我们是可以通过反汇编来看效果的

6.定位new–placement-new

通过上述的学习我们知道,malloc,和operator是不会去调用构造函数的,new会去调用构造函数,而且构造函数是只允许构造出对象时调用,而你的对象创建出来之后是无法调用构造的。

但是如果我们operator new了一块空间(未初始化),但是又想要调用其构造函数,该怎们办呢?

class A
{
public:
	A(int a = 0)
		:_a(a)
	{
		cout << "A(int a = 0)" << endl;
	}
	~A()
	{
		cout << "~A()" << endl;
	}
private:
	int _a;
};
int main()
{
	A* pa = (A*)operator new(sizeof(A));
	//pa->A();//error错误调用方式,构造函数只允许构造时进行调用
	new(pa)A; // 定位new,placement-new,显示调用构造函数初始化这块对象空间
	A* pb = (A*)operator new(sizeof(A));
	new(pb)A(3);
    A* pc = new A;
	new(pc)A(3);
	// 等于 delete p
	pa->~A(); // 析构函数可以显示调用
	operator delete(pa);
	pb->~A();
	operator delete(pb);
    delete pc;
	return 0;
}

大家要知道定位new哦,他是一种对已经创建出来的对象,还能继续调用其构造函数的方式。

7.`malloc`/`free`和`new`/`delete`的区别

常考面试题

8.再次理解内存泄漏

首先大家先想一想这个问题:内存泄漏是指针丢了还是内存丢了?

内存管理中,内存是用指针去维护的,当你的动态内存空间还没有释放时,你已经把指针弄丢了,那么你将无法控制这段空间,进而导致内存泄漏。

什么是内存泄漏,内存泄漏的危害

什么是内存泄漏:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而 造成了内存的浪费。

内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会 导致响应越来越慢,最终卡死。

到此这篇关于C++图文并茂分析讲解内存管理的文章就介绍到这了,更多相关C++内存管理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++11中的变长模板的示例详解

    C++11中的变长模板的示例详解

    C++中的变长模板真的是又臭又长,晦涩难懂,但是确实有些STL库就是这么写的。本文就来和大家聊聊C++11中这些变长模块的使用,需要的可以参考一下
    2023-02-02
  • C/C++ Zlib库封装MyZip压缩类的详细过程

    C/C++ Zlib库封装MyZip压缩类的详细过程

    在软件开发中,文件的压缩和解压缩是一项常见的任务,而ZIP是一种被广泛应用的压缩格式,本文将聚焦于一个简化的C++实现,通过分析代码,我们将深入了解其设计和实现细节,感兴趣的朋友一起看看吧
    2023-11-11
  • C语言快速实现扫雷小游戏

    C语言快速实现扫雷小游戏

    这篇文章主要为大家详细介绍了C语言实现扫雷小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-10-10
  • C++浅析STL 迭代器 容器的使用

    C++浅析STL 迭代器 容器的使用

    这篇文章主要介绍了C++ STL、迭代器、容器,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-07-07
  • C语言实现外卖管理系统

    C语言实现外卖管理系统

    这篇文章主要为大家详细介绍了C语言实现外卖管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-11-11
  • Qt5.9程序打包发布的实现

    Qt5.9程序打包发布的实现

    本文主要介绍了Qt5.9程序打包发布的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-05-05
  • FFRPC应用 Client/Server使用及原理解析

    FFRPC应用 Client/Server使用及原理解析

    这篇文章主要介绍了FFRPC应用 Client/Server使用及原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-08-08
  • C++读写Excel的实现方法详解

    C++读写Excel的实现方法详解

    本篇文章是对C++读写Excel的实现方法进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • C++使用string的大数取模运算(5)

    C++使用string的大数取模运算(5)

    这篇文章主要为大家详细介绍了C++使用string的大数取模运算,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-09-09
  • 解析之C++的列表初始化语法

    解析之C++的列表初始化语法

    有朋友在使用std::array时发现一个奇怪的问题:当元素类型是复合类型时,编译通不过。按说std::array和原生数组的行为几乎是一样的,可为什么当元素类型不同时,初始化语法还会有差别?这篇文章会介绍这个问题的原理,以及正确的解决方式。
    2021-05-05

最新评论