c++初阶------c++内存管理

作者前言

🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂
​🎂 作者介绍: 🎂🎂
🎂 🎉🎉🎉🎉🎉🎉🎉 🎂
🎂作者id:老秦包你会, 🎂
简单介绍:🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂
喜欢学习C语言、C++和python等编程语言,是一位爱分享的博主,有兴趣的小可爱可以来互讨 🎂🎂🎂🎂🎂🎂🎂🎂
🎂个人主页::小小页面🎂
🎂gitee页面:秦大大🎂
🎂🎂🎂🎂🎂🎂🎂🎂
🎂 一个爱分享的小博主 欢迎小可爱们前来借鉴🎂


内存划分

在这里插入图片描述
内存的划分是为了满足不同的数据,有不同的存储需求,各区域满足不同的需求,
需要注意的是,代码段是不能被修改的,如果是强行修改就是这样
在这里插入图片描述
我们来做一道题

int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
 static int staticVar = 1;
 int localVar = 1;
 int num1[10] = { 1, 2, 3, 4 };
 char char2[] = "abcd";
 const char* pChar3 = "abcd";
 int* ptr1 = (int*)malloc(sizeof(int) * 4);
 int* ptr2 = (int*)calloc(4, sizeof(int));
 int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
 free(ptr1);
 free(ptr3);
}

在这里插入图片描述
前五个很简单,答案是C CC AA
后五个的答案是 AAADAB
在这里插入图片描述
我们要注意的是static修饰局部变量(初始化一次,不消毁)、全局变量(只能在本文件内使用,不能跨文件使用)、函数(只能在本文件内使用,不能跨文件使用,
还要const 修饰一些变量比如:
const int a = 10; a变量是常变量,
const int* p = (int)malloc(4);const修饰的是p不能更改,
上面的
const char
pChar3存储的地址是常量区的
在这里插入图片描述
这里的答案是 40 5 4/8 5 4 4/8

C++内存管理方式

前面我们学习了C语言的动态内存开辟,那c++也有自己的动态内存开辟
通过newdelete操作符进行动态内存管理。

#include <iostream>
using namespace std;
int main()
{
	//动态内存开辟
	int* p = new int;//一个整形空间
	int* p1 = new int[10];//十个整形空间

	//初始化
	int* p2 = new int(1);
	int* p3 = new int[10]{1, 2, 3};

	//释放
	delete p;
	delete[] p1;
	delete p2;
	delete[] p3;

	return 0;
}

主要是为了解决动态申请自定义类型对象初始化问题

#include <iostream>	
using namespace std;
class ListNode
{
public:
	ListNode(int a = 30)
	{
		_a = a;
		_Node = nullptr;
	}
	void LinkNode(const ListNode& newnode)
	{
		_Node = newnode._Node;
	}
private:
	int _a;
	ListNode* _Node;
};
int main()
{
	ListNode* p1 = new ListNode;
	ListNode* p = new ListNode(60);//可以这样初始化
	ListNode* p2 = new ListNode[3]{ 6,5,4 };//这里是隐式类型转换,也可以使用匿名对象
	p1->LinkNode(*p);

	return 0;
}

new的本质:开空间+调用构造函数初始化,
但是使用new来开辟很大空间也会失败,这个异常必须捕获,不然就会有意外

异常捕获

int main()
{
	char* p = new char[0x7ffffff];
	cout << p << endl;
}

会输出很多东西,无限循环,但是没有报错

int main()
{
	//异常捕获
	try//如果try里面报错了就会运行catch里面的内容
	{
		char* p = new char[0x7ffffff];
		cout << (void*)p << endl;
		char* p2 = new char[1024*1024*1024];
		cout << (void*)p2 << endl;
	}
	catch (const exception& e)
	{
		cout << e.what() << endl;
	}
	
}

格式

	try
	{}
	catch (const exception& e)
	{}

释放空间

delete p;//使用于创建一个的时候
delete[] p;//使用于创建多个并且连续的

malloc/free和new/delete的区别

#include<iostream>
using namespace std;
class Stack
{
public:
	Stack(int capacity)
	{
		_top = 0;
		_a = new int[capacity];
		_capacity = capacity;
	}
	~Stack()
	{
		cout << _capacity << endl;
		delete[] _a;
		_a = nullptr;
		_top = 0;
		_capacity = 0;

	}
private:
	int* _a;
	int _top;
	int _capacity;

};
int main()
{
	
	Stack s1(30);
	Stack* p = new Stack(40);
	//delete p;
	free(p);
	return 0;
}

在这里插入图片描述
会发现,无法调用析构函数,是因为free只会释放当前的空间,不会释放_a指向的空间,除非调用析构函数,但是free释放了当前的空间,无法找到哪个空间在哪里,就会出现内存泄漏
在这里插入图片描述

如果我们使用delete就会发现,会调用析构函数,
在这里插入图片描述
new和delete的底层

/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间
失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
*/
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
void *p;
while ((p = malloc(size)) == 0)
     if (_callnewh(size) == 0)
     {
         // report no memory
         // 如果申请内存失败了,这里会抛出bad_alloc 类型异常
         static const std::bad_alloc nomem;
         _RAISE(nomem);
     }
return (p);
}
/*
operator delete: 该函数最终是通过free来释放空间的
*/
void operator delete(void *pUserData)
{
     _CrtMemBlockHeader * pHead;
     RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
     if (pUserData == NULL)
         return;
     _mlock(_HEAP_LOCK);  /* block other threads */
     __TRY
         /* get a pointer to memory block header */
         pHead = pHdr(pUserData);
          /* verify block type */
         _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
         _free_dbg( pUserData, pHead->nBlockUse );
     __FINALLY
         _munlock(_HEAP_LOCK);  /* release other threads */
     __END_TRY_FINALLY
     return;
}
/*
free的实现
*/
#define   free(p)               _free_dbg(p, _NORMAL_BLOCK)

new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是
系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过
operator delete全局函数来释放空间。需要注意的是这里的operator new 和operator delete只和申请空间有关,跟调用析构和构造函数没有关系

为啥不直接调用malloc和free来呢
我们知道malloc开辟失败返回0,c++的自定义类型的对象,创建失败是不能返回0的,所以要抛异常才能符合这个特性,所以这里operator new 和operator delete是为抛异常做的准备的
下面是vs反汇编调用函数,以delete为例:
在这里插入图片描述

所以我们可以看作

在这里插入图片描述

new[]和delete[]
那我们来看看 new[]这个操作符
我们利用上面的stack自定义类型为例,开辟10个stack类型的空间,
大概是申请了10个Stack对象的地址,然后再调用10次构造函数
我们来看看其反汇编
在这里插入图片描述
在这里插入图片描述
当我们查看size的大小的时候
在这里插入图片描述
会发现多了4个字节,
在这里插入图片描述
原因如图:开辟多出来的用来存储对象个数,主要是为了delete操作符准备的.因为我们释放多个对象,我们不用写个数,也就是这个原因,
如果我们创建多个对象,却只释放一个,就会程序崩溃,为啥不是泄漏呢?
因为我们知道创建多个对象,会多开辟4个字节来存储,而开辟一个是不会浪费四个字节的,所以,也就是创建多个对象,只释放一个,是位置释放错了,因为创建一个对象返回的地址就是起始地址,如果创建的是多个对象的,返回的地址不是起始地址,
在这里插入图片描述

需要注意的是,如果类的成员只有内置类型,析构函数可要可不要,使用free释放或者delete都不会报错,

malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。不同的地
方是:

  1. malloc和free是函数,new和delete是操作符
  2. malloc申请的空间不会初始化,new可以初始化
  3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,
    如果是多个对象,[]中指定对象个数即可
  4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
  5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需
    要捕获异常
  6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new
    在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成
    空间中资源的清理

定位new表达式(placement-new)

我们知道构造函数不能显示调用,在创建对象的时候会自动调用构造函数,但是析构函数是可以的
在这里插入图片描述
如果我们想要进行显示调用构造函数,可以通过定位new来实现,
定位new表达式是在已分配原始内存空间调用构造函数初始化一个对象。

使用格式:
new (place_address) type或者new (place_address) type(initializer-list)
place_address必须是一个指针,initializer-list是类型的初始化列表

#include<iostream>
using namespace std;
class Stack
{
public:
	Stack(int capacity = 5)
	{
		_top = 0;
		_a = new int[capacity];
		_capacity = capacity;
	}
	~Stack()
	{
		cout << _capacity << endl;
		delete[] _a;
		_a = nullptr;
		_top = 0;
		_capacity = 0;

	}
private:
	int* _a;
	int _top;
	int _capacity;

};
int main()
{
	Stack* p = new Stack(10);
	new(p)Stack(20);//显示调用构造函数
	delete p;

	return 0;
}

在这里插入图片描述

相关推荐

  1. C++:浅识内存管理

    2024-03-24 19:08:02       17 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-24 19:08:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-24 19:08:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-24 19:08:02       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-24 19:08:02       20 阅读

热门阅读

  1. vue 控制窗口禁止缩放,已解决

    2024-03-24 19:08:02       17 阅读
  2. 算法学习系列(四十二):最短路模型

    2024-03-24 19:08:02       19 阅读
  3. C++例子

    C++例子

    2024-03-24 19:08:02      18 阅读
  4. pytest 之数据驱动

    2024-03-24 19:08:02       22 阅读
  5. 八个 C++ 开源项目,帮助初学者进阶成长

    2024-03-24 19:08:02       18 阅读
  6. 基于单片机的农业智能节水灌溉系统设计

    2024-03-24 19:08:02       17 阅读
  7. 数学建模(Topsis python代码 案例)

    2024-03-24 19:08:02       20 阅读
  8. Pytorch:torch.cuda.empty_cache()

    2024-03-24 19:08:02       16 阅读