【C++】new和delete

new和delete

  • 1. 为什么会有new和delete?
  • 2. new和delete
    • 2.1 存储内置类型数据
    • 2.2 存储自定义类型数据
  • 3. new和delete是如何申请和释放空间的?
  • 4. 定位new(placement-new)
  • 5. 内存泄漏
  • 6. malloc/free和new/delete的区别


1. 为什么会有new和delete?

C语言用malloc、calloc、realloc、free来进行动态内存管理,而C++用new和delete来进行动态内存管理。当然因为C++兼容C,所以在C++中同样可以使用malloc等函数。
那为什么C++会有new和delete?

class ListNode
{
public:
	ListNode(int x)
		:_val(x)
		, _next(nullptr)
	{
		cout << "ListNode()" << endl;
	}
	~ListNode()
	{
		cout << "~ListNode()" << endl;
		if(_next)
		{
			free(_next);
		}
	}
public:
	int _val;
	ListNode* _next;
};
//这是用malloc申请节点
ListNode* BuyListNode(int x)
{
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	newnode->_next = NULL;
	newnode->_val = x;
	return newnode;
}
//这是用new申请结点
ListNode* BuyListNode(int x)
{
	ListNode* newnode = new ListNode (x);
	delete newnode;
	return newnode;
}

当数据类型是内置类型时,malloc申请节点不会初始化,calloc会全部初始化为0;当数据类型是自定义类型时,malloc和calloc用起来就有点麻烦了。因此C++提出自己的动态内存管理方式:new和delete。new可以开辟空间的同时调用构造函数初始化,delete释放空间的同时会调用析构函数。


2. new和delete

2.1 存储内置类型数据

int main()
{
	int* n1 = new int;//new + 类型,申请一个int类型的空间
	int* n2 = new int(1);//new + 类型 + (n),申请空间后,对其初始化
	char* n3 = new char[10]{ 'a','b','c' };//申请10个字符的空间,并初始化前三个,其余置为0
	int* n4 = new int[10]{ 1,2,3 };

	delete n1;
	delete n2;
	delete[]n3;
	delete[]n4;
	//注意:
	// 1.new和delete是操作符,malloc和free是函数
	// 2.申请和释放单个元素的空间用new和delete,申请和释放连续空间时用new[]和delete[]。
	// 3.单个元素初始化用()圆括号,连续的元素初始化用{ }大括号
	return 0;
}

补充
内置类型没有初始化,会是随机值。
【C++】new和delete_第1张图片

2.2 存储自定义类型数据

new和delete申请自定义类型时,会调用其构造函数和析构函数。这也是new和delete与malloc和free最大的区别。

//例子
class ListNode
{
public:
	ListNode(int x)
		:_val(x)
		, _next(nullptr)
	{
		cout << "ListNode()" << endl;
	}
	~ListNode()
	{
		cout << "~ListNode()" << endl;
		free(_next);
	}
public:
	int _val;
	ListNode* _next;
};
int main()
{
	ListNode* n1 = new ListNode(1);
	delete n1;
	return 0;
}

调用情况
【C++】new和delete_第2张图片

//例子2
ListNode* n2 = new ListNode[10]{ 1,2,3,4,5,6, 7,8,9,10};
//也可以这样初始化new ListNode[10]{ListNode(1),ListNode(2),ListNode(3),……};
delete []n2;
//调用10次构造函数和析构函数。ListNode没有提供默认构造函数,所以必须全部初始化。
//如果有提供默认构造函数,就可以不完全初始化,其余的ListNode会调用默认构造	

调用情况
【C++】new和delete_第3张图片

注意
new和delete,malloc和free一定要匹配使用,不能用free去释放new的变量,不能用delete去释放malloc的变量。


3. new和delete是如何申请和释放空间的?

  1. new和delete通过调用系统提供的全局函数operator new和operator delete来实现的。new在底层调用operator new来申请空间,delete在底层调用operator delete来释放空间。

【C++】new和delete_第4张图片

【C++】new和delete_第5张图片
所以new = operator new + 自定义类型调用构造函数delete = 先调用析构函数释放资源 + 再operator delete

  1. 那么operator new是如何开辟空间,operator delete是如何释放空间的?
    通过查看operator new在C++库的源代码,发现其底层其实就是malloc,通过查看operator delete在C++库的源代码,发现其底层是free。
    【C++】new和delete_第6张图片
    【C++】new和delete_第7张图片
  2. 面向对象语言处理失败,不喜欢用返回值,更建议用抛异常。operator new实际是通过malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。

4. 定位new(placement-new)

  1. 定义

定位new表达式是在已经分配的内存空间调用构造函数初始化一个对象。

  1. 格式
new(指针)类型或者new(指针)类型(初始值)
  1. 例子

(1)内置类型
【C++】new和delete_第8张图片
(2)自定义类型
【C++】new和delete_第9张图片

  1. 应用场景
    池化技术。简单讲就是每次new需要频繁申请和释放内存,提前申请一大块内存放在池子,每次申请都找池子要,池子不够再找内存申请,从内存池申请空间效率更高点,所以池化技术可以提高效率。但是内存分配的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。

5. 内存泄漏

当我们申请空间后,忘记或者因为程序错误而忘记释放空间,可能导致内存泄漏。内存泄漏的危害很大,尤其是对一些长期运行的程序,会导致其响应越来越缓慢。
那么该怎么避免内存泄露呢?第一,养成良好的代码习惯,申请的空间匹配去释放;第二,利用智能指针来管理资源。


6. malloc/free和new/delete的区别

共同点:它们都是从堆上申请空间,且都需要手动释放。
不同点:
(1)malloc和free是函数,new和delete是操作符。
(2)malloc不会初始化,new会初始化。
(3)如果申请的空间来存储自定义类型,new会调用其构造函数,delete会调用其析构函数,而malloc和free不会。
(4)malloc申请空间失败会返回null,而new申请空间失败会抛异常。
(5)malloc返回值为void*,需要强制类型转换,而new不需要。
(6)在申请空间时,malloc需要手动计算申请空间大小,而new不需要,只需要接类型和个数即可。

你可能感兴趣的:(c++,开发语言)