C++经典面试题 | new/new[]和delete/delete[]的区别原理
1.先来谈谈new和delete
在C++中堆内存的分配和释放是通过new和delete来操作的,它们和C语言的malloc和free有什么区别呢?
(1)new的底层也是通过malloc来开辟内存的
new比malloc多一项功能,就是开辟完内存,还可以进行初始化操作
如下:
int *p = new int(10);
上面是new的基本操作,10代表堆上开辟的整形内存的初始值;如果是自定义类类型的话,如下:
Test *p = new Test();
这个语句不仅会在堆上开辟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内存开辟释放行为,比如说实现内存池。
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;
以上四条代码,也不存在任何问题,因为这个类没有提供析构,也就是说Test类对象不需要进行任何有效的析构,那么delete就只剩完成free的功能就行了,那什么时候必须配对使用呢?看看下面的Test类型:
class Test
{
public:
Test(){}
~Test(){}
private:
int ma;
};
Test *p = new Test; delete p; // =》 OK!
Test *p = new Test; delete []p; // =》 程序崩溃!
Test *p = new Test[10]; delete []p; // =》 OK!
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从逻辑上来说,是可以混用的,但是最好不要这样做,这样的代码没人喜欢看,而且隐患很大!