一、new和delete
1、在C++中堆内存的分配和释放是通过new和delete 来操作的,他们和C语言的malloc和free有什么区别呢?
new的底层也是通过malloc来开辟内存的,new比malloc 多一项功能,就是开辟完内存,还可以进行初始化操作,
如:int* p=new int(10);
上面是new的基本操作,10代表堆上开辟的整型内存的初始值,如果是自定义类类型的话,如下:
Test *p=new Test();
这个语句不仅会在堆上开辟Test类型大小的一块内存,还会调用Test类的默认构造函数构造出一个对象出来,这些都是malloc办不到的
2、delete 比free 多一项功能就是在释放内存之前,还可以析构指针指向的对象,new和delete配对使用,尽量不要交叉使用,以免产生不可预期的问题。
3、new开辟内存失败是抛出 bad_alloc类型的异常,因此代码上要捕获该类型的异常才能正确的判断堆内存是否分配成功,malloc内存开辟失败返回的是NULL指针。
4、new和delete不仅仅是运算符,它们实际上是运算符重载函数的调用,对应的函数名是operator new和operator delete,可以在全局或者类的作用域中提供自定义的new和delete运算符的重载函数,以改变默认的malloc和free内存开辟释放行为,比如说实现内存池。
二、什么时候用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;
以上4条代码,也不存在任何问题,因为这个类没有提供析构函数,也就是说Test类对象不需要进行任何有效的析构,那么delete就只剩完成free的功能就行了,那么什么时候需要配对使用呢?看看下面的代码:
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[]的时候,它是从哪里释放内存的?
为什么它会减4个字节开始释放呢? 因为定义了析构函数,在释放内存之前需要在内存中析构对象,你写个delete[]p,编译器就认为这里有很多对象需要析构,多少个对象呢?记录对象个数的数字就在对象内存上面的4个字节存放,因为它从那里开始释放内存了,这肯定是发生错误的。
那么 Test *p=new Test[10]; delete p 呢?如下图:
因此,当自定义类型时,而且提供了析构函数的时候,那么new和delete千万不要混用,会导致对象的析构和内存释放有问题,除此之外,new和delete 从逻辑上来说,是可以混用的,但是最好在编程时不要这么做,而且隐患很大。