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;
}
补充
内置类型没有初始化,会是随机值。
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;
}
调用情况
//例子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会调用默认构造
调用情况
注意
new和delete,malloc和free一定要匹配使用,不能用free去释放new的变量,不能用delete去释放malloc的变量。
3. new和delete是如何申请和释放空间的?
- new和delete通过调用系统提供的全局函数operator new和operator delete来实现的。new在底层调用operator new来申请空间,delete在底层调用operator delete来释放空间。
所以new = operator new + 自定义类型调用构造函数,delete = 先调用析构函数释放资源 + 再operator delete。
- 那么operator new是如何开辟空间,operator delete是如何释放空间的?
通过查看operator new在C++库的源代码,发现其底层其实就是malloc,通过查看operator delete在C++库的源代码,发现其底层是free。
- 面向对象语言处理失败,不喜欢用返回值,更建议用抛异常。operator new实际是通过malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。
4. 定位new(placement-new)
- 定义
定位new表达式是在已经分配的内存空间调用构造函数初始化一个对象。
- 格式
new(指针)类型或者new(指针)类型(初始值)
- 例子
(1)内置类型
(2)自定义类型
- 应用场景
池化技术。简单讲就是每次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不需要,只需要接类型和个数即可。