C++释放new分配内存时带方括号delete[]和不带方括号delete的区别
摘要
在C++中通过new动态分配的内存,必须要要用delete进行释放,而使用使用new[]申请的内存释放时,标准做发用delete[],但有时用delete也能正常释放。那到底delete带方括号[]和不带方括号[]有什么区别呢?以下通过实例代码,测试一下C++中通过new[] 创建的内存在释放时是否一定需要在delete后加[]
基本数据类型
对于基本数据类型(如char, int, unsigned int, short int等),如果动态申请一个数组类型,如下:
int *a = new int[10 * 1024 * 1024];
...
delete a; // 方式1,不带方括号
int *b = new int[10 * 1024 * 1024];
...
delete [ ] b; //方式2,带方括号
以上示例代码,使用了两种方法分配了一个10M int类型的数组,实际占用空间是40M字节。
肯定会不少人认为方式1存在内存泄露,然而事实上是不会!
通过观察任务管理器中,进程占用的内存可以看到,以上两种方式在申请内存后,进程占用的内存会增加40MB,而释放后就会还原到分配前的内存占用。
针对基本数据类型,方式1和方式2均可正常工作,因为:基本的数据类型对象没有析构函数,并且new 在分配内存时会记录分配的空间大小,则delete时能正确释放内存,无需调用析构函数释放其余指针。因此两种方式均可。
自定义数据类型
对于自定义的class(类),通过new申请了一个对象数组,返回一个指针,对于此对象数组的内存释放,需要做两件事情:一是释放最初申请的那部分空间,二是调用析构函数完成清理工作。
案例1
我们来看一个示例:
#include iostream
using namespace std;
class TestClass
{
public:
int a;
};
int main()
{
TestClass* p1 = new TestClass[10 * 1024 * 1024];
delete p1; //方式1,不带[]
TestClass* p2= new TestClass[10 * 1024 * 1024];
delete[] p2; //方式2,带[]
return 0;
}
以上代码两种释放方式,都能正常释放,通过观察进程的内存占用,也能看出两种方法分配内存后进程占用的内存相应增加,两种方法delete后,内存都能相应的减少。
案例2
我们将案例1稍微修改一下,代码如下:
#include iostream
using namespace std;
class TestClass
{
public:
int* p;
TestClass()
{
p = new int[1];
}
~TestClass()
{
delete[] p;
}
};
int main()
{
TestClass* p1 = new TestClass[10 * 1024 * 1024];
delete p1; //方式1,不带[]
TestClass* p2= new TestClass[10 * 1024 * 1024];
delete[] p2; //方式2,不带[]
return 0;
}
以上代码,将TestClass中修改为在类中有动态分配内存,并在析构时释放。方式1,直接会报如下异常信息:
继续执行会报如下错误:
而方式2就能正常释放,并且从任务管理器观察的内存变化情况来看,占用的内存也都相应的被释放掉了。
对于内存空间的清理,由于申请时记录了其大小,因此无论使用delete还是delete[]都能将这片空间完整释放,而问题就出在析构函数的调用上,当使用delete时,仅仅调用了对象数组中第一个对象的析构函数,而使用delete []的话,将会逐个调用析构函数。
异常原因
我们再来看如下代码
#include iostream
using namespace std;
class TestClass
{
public:
TestClass() { cout constructor endl; }
~TestClass() { cout destructor endl; }
};
int main()
{
const int NUM = 3;
TestClass* p1 = new TestClass[NUM];
cout hex p1 endl; //输出P1的地址
// delete[] p1;
delete p1; //方式1,不带[]
cout endl;
TestClass* p2 = new TestClass[NUM];
cout p2 endl; //输出P2的地址
delete[] p2;
return 0;
}
输出结果为:
constructor
constructor
constructor
001174D4
destructor
此时就直接报前文所说的异常了。
而将方式1的代码屏蔽掉,直接执行方式2的代码,输出结果如下:
constructor
constructor
constructor
00A13514
destructor
destructor
destructor
可以看到,不加[]时只调用了一次析构函数,然后就报异常了,而加了[]调用了上次析构函数。
总结
通过实例代码,测试了C++中通过new[] 创建的内存在释放时是否需要在delete后加[]。从测试结果来看,如果是基本数据类型,delete时加方括号和不加方括号都是一样的效果。如果是自定义的class,分两种情况:class中有动态分配的资源和没有动态分配的资源,有动态分配的资源时,也就是需要通过析构函数来释放动态分配的资源,则必须使用delete[],否则就会报异常(也可能开发环境并不会报异常,但由于没有成功调用析构函数释放资源,会造成内存泄漏);而没有动态分配的资源时,delete和delete[]都能正常释放掉内存。所以,通过new[]创建的内存,在释放时,最好还是使用delete[]的方式释放更加保险。
更新于:4个月前相关文章
- 【说站】java start()和run()的区别
- 【说站】JavaScript引发内存泄漏的情况
- 【说站】java sleep()和wait()的区别
- 【说站】Java守护线程和用户线程的区别
- 【说站】python TCP和UDP协议的区别分析
- 【说站】java io和nio的区别
- 【说站】java懒汉和饿汉模式的区别
- 【说站】ps带cc和不带cc有什么区别
- 【说站】java对象分配内存的两种类型
- 【说站】java内存溢出的四种情况
- 【说站】java抽象类和接口的区别探究
- 【说站】java如何检查内存泄漏
- 【说站】java内存泄漏
- 【说站】java内存泄漏的解决方法
- 【说站】java中不同变量的区别
- 【说站】java throw和throws的区别
- 【说站】Java内存分配是什么
- 【说站】java异步和同步的区别
- 【说站】php7与php5的区别
- 【说站】java集合和数组的区别