1.先来谈谈new和delete
在C++中堆内存的分配和释放是通过new和delete来操作的,它们和C语言的malloc和free的区别:
(1)new的底层也是通过malloc来开辟内存的,但new比malloc多一项功能,在开辟完内存还可以进行初始化操作。
int *p = new int(8);
上面是new的基本操作,8代表堆上开辟的整形内存的初始值;如果是自定义类类型的话。
Test *p = new Test(); // Test是一个事先定义好的类
该new语句在堆上开辟完Test类型大小的内存后,还会调用Test类的默认构造函数构造一个对象出来;malloc智能一次开辟某一大小的的内存空间,无法进行构造初始化操作。
(2)delete比free多一项功能就是在释放内存之前,还可以析构指针指向的对象;new和delete配对使用,new[]和delete[]配对使用,尽量不要交叉使用,虽然有时可以正常运行,但是即是不规范的代码书写,同时也容易造成潜在的风险。
(3)new开辟内存失败是抛出bad_alloc类型的异常,因此代码上要捕获该类型的异常才能正确的判断堆内存是否分配成功;malloc内存开辟失败返回的是nullptr指针。
(4)new和delete不仅仅是运算符,它们实际上是运算符重载函数的调用,对应的函数名是operator new和operator delete。可以在全局或者类的作用域中提供自定义的new和delete运算符的重载函数,以改变默认的malloc和free内存开辟释放行为,比如说实现内存池。
(5)申请数组时new[]一次分配所有内存,多次调用构造函数,搭配使用 delete[ ], delete[ ]多次调用析构函数,销毁数组中的每个对象。而 malloc则只能 sizeof(int) * n。
(6)new返回的是指定对象的指针,而 malloc返回的是void,因此 malloc的返回值一般都需要进行类型转化
2.用new[]申请的内存,何时可以用delete释放?
new和new[]的底层都是通过malloc来开辟内存的,delete和delete[]底层都是通过free来释放内存的,那么C++里面为什么把单个元素的内存开辟释放和数组的内存开辟释放分开呢?因为在C++里面,开辟内存和构造对象是一起发生的,析构对象和释放内存是一起发生的。
int *p = new int; delete p;
int *p = new int; delete []p;
int *p = new int[10]; delete []p;
int *p = new int[10]; delete p;
看上面的示例代码,对于内置类型来说,这样混用是可以的,因为对于内置类型没有什么所谓的构造和析构,因此在这里的内存管理和调用malloc,free的含义是一样的,不存在什么问题,但是请大家在实际的编码过程中不要这样写,毕竟这不是好的编码规范!
对于如下的类类型:
class Test{
public:
Test(){}
private:
int ma;
};
Test *p = new Test; delete p;
Test *p = new Test; delete []p;
Test *p = new Test[10]; delete []p;
Test *p = new Test[10]; delete p;
以上四条代码,也不存在任何问题,因为这个类没有提供析构,也就是说A类对象不需要进行任何有效的析构,那么delete就只剩完成free的功能就行了,那什么时候必须配对使用呢?看看下面的Test类型:
class Test
{
public:
Test(){}
~Test(){}
private:
int ma;
};
Test *p = new Test; delete p; // 正确
Test *p = new Test; delete []p; // 报错
Test *p = new Test[10]; delete []p; // 正确
Test *p = new Test[10]; delete p; // 报错
这个Test类和上面的有什么不一样的地方吗?当然有了,此时有自定义的析构函数了,那么我们来分析一下new和delete的具体操作是怎么进行的!
Test *p = new Test; delete []p;在内存上开辟了4字节的内存,如下:
但是当你去delete[]p;的时候,它是从哪里释放内存的?看看图:
为什么它会减四个字节开始释放内存呢?
因为定义了析构函数,在释放内存之前需要在内存中析构对象,你写个delete[]p,编译器就认为这里有很多对象要析构,多少个对象呢?记录对象个数的数字就在对象内存上面的4个字节存放,因此它从那里开始释放内存了。
Test *p = new Test[10]; delete p;
这句错误的原因可以根据上面的解释你就可以自行分析一下了。
因此,当时自定义类类型,而且提供了析构函数的时候,那么new和delete千万不能混用,会导致对象的析构和内存释放有问题;除此之外,new和delete从逻辑上来说,是可以混用的,但是最好不要这样做,这样的代码没人喜欢看,而且隐患很大!