(C++)内存管理:new与delete 异常 定位new

# 0 C++的内存管理

我们在c语言中常用的内存管理方法为调用malloc和free等函数,
从堆上申请空间使用,完毕后释放。

随着C++的创新与发展,原本的内存管理方式有些无法满足需求,
于是创造出新的关键字来管理内存。

# 1 new

# 1.1 new用法

new的用法十分简便。

int main()
{
	// 开辟单个数据空间
	int* p1 = new int;
	
	// 开辟多个数据空间
	int* p2 = new int[10];

	return 0;
}

在new的后面加上开辟出的空间存放的内容的类型,后面中括号内写数据数量。

注意:
后面的数字表示数据的个数,而不是大小。
前面是int,所以这里开辟的是40个字节。
以往用malloc的话,这个总字节需要我们自己算,用new就不需要了。

上面是代替以往的malloc,如果想初始化,也就是达成以往的calloc的功能,
对于单个数据空间,只需在后面加上小括号,括号中写用于初始化的值,
对于多个数据空间,则将小括号换成大括号。

int main()
{
	// 开辟单个数据空间,并初始化
	int* p1 = new int(1);

	// 开辟多个数据空间,并初始化
	// 如果用于初始化的值填不满整个数据空间,那么后面的值将补0
	int* p2 = new int[3]{ 1,2,3 };
	
	return 0;
}

# 1.2 new自动调用构造函数

new不仅仅是申请空间,如果数据类型是类类型的话,还会去调用该类的构造函数。
以往的malloc无法做到,这就是创造出new的主要意义。

用链表节点举例:
以前创建一个新的链表节点,需要有newnode函数,有init函数,
现在只需要在链表这个类内写好相应的构造函数即可。

class List
{
public:
	List(int val)
	{
		_val = val;
		_next = nullptr;
	}
private:
	int _val;
	List* _next;
};

int main()
{
	// 直接将List当作内置类型来用
	// new会自动去调用构造函数
	List* newnode = new List(1);

	return 0;
}

这样一来,开辟空间存放类类型数据在和存放内置类型数据就没有什么区别了。

# 1.3 new失败

以往malloc失败,函数的返回值是NULL。
而new如果失败,将会抛异常。
异常必须被捕获。

异常会影响执行流,如果抛了异常,就不会执行下一条语句,
会跳转到捕获的地方。

int main()
{
	try
	{
	//new相关语句
		int* p = new int[10];
	//delete相关语句
		delete[] p;
	}
	catch(const exception& e)
	{
		cout << e.what() << endl;
	}


	return 0;
}

简单来说,就是将new和delete相关的语句都写进这样一个格式之内。
多的暂且不提。

# 1.4 定位new

对已经开辟了的划分出的空间,进行初始化,需要用到定位new。
适用于频繁申请小对象的场合。

在内存池相关的场合会经常使用。
内存池,简单来说,就是每次向堆申请空间时,会多申请额外的空间,
这部分额外空间就作为内存池。
之后再需要向堆申请空间时,直接从内存池里拿,无需再向堆申请,提高效率。

内存池中的空间就属于已经开辟了,但没有初始化的空间。
需要用到定位new来显式的调用构造函数。

class A
{
public:
	A(int a = 1)
	{
		_a = a;
	}

	void Print()
	{
		cout << _a << endl;
	}
private:
	int _a;
};

int main()
{
	// 如果只是开辟空间,没有初始化
	// 那么后续要如何完成对这块已开辟的空间的初始化呢?
	A* p = (A*)malloc(sizeof(A));
	p->Print();

	// 不允许这样显式的调用构造函数
	//p->A();

	// 但是可以通过定位new来显式调用构造函数
	new(p)A(2);

	p->Print();
	return 0;
}

在这里插入图片描述

# 2 delete

有开辟当然就要有释放。
同样有新关键字delete,用于代替free的功能。

# 2.1 delete用法

delete用于与new进行匹配,
凡是new开辟出的空间,都需要通过delete来释放,
凡是new[]开辟出的空间,都需要通过delete[] 来释放,delete[]中间不需要加数据个数。

int main()
{
	int* p1 = new int;
	int* p2 = new int[10];

	delete p1;
	delete[] p2;
	
	return 0;
}

# 2.2 delete自动调用析构函数

与new相对,delete也不仅仅是释放空间,在释放空间之前,也会调用该类的析构函数。

一定要注意new与delete,new[]与delete[]的匹配。
不然可能会出现问题。

new[]出的空间,会在这串空间前面连续的开辟四个字节,
用于存放new出的空间所对应的数据个数。

这个值会在delete[]的时候用到,先调用相应次数的析构函数,最后释放空间。

举个例子:
new[]出的空间如果delete就会因从申请的空间局部释放而出错。
因为申请的空间是包含前面那个存放个数的数据的,
而delete认为你申请的是单个空间,导致空间的局部释放。

总之,注意new和delete,new[]和delete[]的配套使用。

  • 50
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值