文章目录
# 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[]的配套使用。