首页 程序笔记 C++释放new分配内存时带方括号delete[]和不带方括号delete的区别

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[]的方式释放更加保险。

2

站心网

摘要 在C++中通过new动态分配的内存,必须要要用delete进行释放,而使用使用new[]申请的内存释放时,标准做..

为您推荐

在 Javascript 中 声明时用 var 与不用 var 的区别

avascript 声明变量的时候,虽然用 var 关键字声明和不用关键字声明,很多时候运行并没有问题,但是这两种方式还是有区别的。可以正常运行的代码并不代表是合适的代码。var num = 1;是在当前域中声明变量。如果在方..

float 与 double 类型区别

float 单精度浮点数在机内占 4 个字节,用 32 位二进制描述。double 双精度浮点数在机内占 8 个字节,用 64 位二进制描述。浮点数在机内用指数型式表示,分解为:数符,尾数,指数符,指数四部分。数符占 1 位二进制..

CPU、GPU 和 TPU 之间有什么区别?

什么是 CPU、GPU 和 TPU?它们都是用于计算任务的处理器芯片。可以把你的大脑想象成一台计算机,能够完成诸如阅读书籍或解决数学问题的任务。每一项活动都类似于一个计算任务。例如,当你用手机拍照、发送短信或打开..

ASP.NET MVC与Web Forms的区别

ASP.NET MVC 和 ASP.NET Web Forms 是 .NET 平台上用于构建 Web 应用程序的两种不同开发模式。它们在架构、开发方式和适用场景上有显著区别。1. 架构模式ASP.NET Web Forms:基于事件驱动的控件模型,即“页面..

C#中的String和StringBuilder的区别

在 C# 中,String 和 StringBuilder 都是用于处理字符串的类,但它们在性能、可变性和使用场景上有显著区别。1. 可变性(Mutability)String:不可变。创建后,字符串的内容就不能更改。每次对 String 执行拼接、替..

.NET C#中的IEnumerable和IEnumerator的区别

在 .NET 中,IEnumerable 和 IEnumerator 是与集合迭代相关的两个接口,但它们的职责和用途有所不同。简单来说,IEnumerable 是集合的抽象,用于使集合可以被枚举;而 IEnumerator 则负责具体的枚举操作,实现实际的..

MySQL 5.x和MySQL 8.x数据库的区别

MySQL 是开源关系型数据库的代表,广泛应用于不同规模的 Web 和企业应用中。从 MySQL 5.x 到 MySQL 8.x 的升级带来了大量功能改进和性能提升。为了帮助大家更直观地理解两者的区别,本文将通过详细介绍并结合实际的 ..

C# Const 和 ReadOnly的区别

C#中的const和readonly虽然都能用于定义常量,但它们之间存在一些关键的区别。以下是具体分析:初始化位置const:必须在声明的同时赋值。这意味着const变量的值在编译时就已经确定。readonly:可以在声明处或构造函..

Asp.Net Core进程内托管 和 进程外托管的区别

在ASP.NET Core中,托管模型决定了应用程序如何运行及其与Web服务器交互的方式。主要有两种托管模式:进程内托管(In-Process Hosting)和进程外托管(Out-of-Process Hosting)。每种模式都有其独特的优势和适用场..

数据库SQL Server2014和SQL Server2019的区别和如何选择?

SQL Server 2014和SQL Server 2019是微软公司发布的两个版本的数据库管理系统,它们在性能、安全性以及可扩展性等方面各有特点。在选择这两个数据库版本时,需要根据系统需求、预算状况以及技术团队的熟悉程度等因素..

ViewData和ViewBag的区别,ViewData和ViewBag哪个更好

ViewData是Key/Value字典集合,从Asp.net MVC 1 就有了,是基于Asp.net 3.5 framework的,ViewData比ViewBag快,在ViewPage中查询数据时需要转换合适的类型。 而ViewBag是dynamic类型对像,是从ASP.NET MVC3 才有的..

Parallel.For和普通For的区别

Parallel类是.NET 4中新增的抽象线程类。Parallel.For()方法类似于C#的for循环语句,也是多次执行一个任务。但是使用Parallel.For()方法,可以并行运行。对于Parallel.For、Parallel.Foreach的使用应该要特别小心,..

Blazor的5种render-mode的区别

Blazor 是一个基于 .NET 平台的 Web 应用程序开发框架,它支持多种渲染模式,包括:Server:在服务器端执行应用程序逻辑和 UI 渲染,然后通过 SignalR 技术将 UI 更新推送到客户端。这种模式适合于需要与后端服务器..

Blazor使用内存中状态容器服务保存和验证登陆状态

想用Blazor做一个简单的登录验证。模式是render-mode="ServerPrerendered"。在登录页面登录成功后需要保存类似.NET MVC网站的服务端session的状态。网上一些简单的做法是登录成功后把用户信息存在LocalStorage或者Se..

什么是微服务架构?它与单体应用程序架构有什么区别?如何在.NET中实现微服务架构?

微服务架构是一种软件架构风格,通过将应用程序拆分为一组小型、自治的服务来构建应用程序。每个服务都专注于解决特定的业务功能,并通过轻量级的通信机制进行交互。这些服务可以独立开发、部署和扩展,可以使用不同..

解释一下MVC(Model-View-Controller)和MVVM(Model-View-ViewModel)模式,并比较它们之间的区别。

MVC(Model-View-Controller)和MVVM(Model-View-ViewModel)是两种常见的软件架构模式,用于组织和分离应用程序中的不同部分。它们有一些相似之处,但也有一些明显的区别。MVC模式Model(模型):负责处理应用程序..

.NET Core和Framework的区别?

.NET Core和.NET Framework是微软开发的两个不同的软件开发平台,用于构建Windows应用程序和服务。它们有一些重要的区别:跨平台支持.NET Core 专注于跨平台支持,可以在Windows、Linux和macOS等多种操作系统上运行..

程序员开发人员常用工具和网站

给大家分享一些程序员开发人员常用工具和网站,我相信这里总有一款工具适合你。排名不分先后~Visual Studio CodeVisual Studio Code官网地址:https://code.visualstudio.com/Visual Studio Code重新定义和优化了代..

类似sms-activate的国外手机验证码接码平台

很多网站需要通过手机验证码才能注册成功,例如最近大火的ChatGPT的官网OpenAI只能使用国外手机号注册。这里推荐几个可以接收国外手机验证码的平台。sms-activatehttps://sms-activate.org/cn站点推荐:ChatGPT手机..

百度文心一言邀请码如何获得?

3月16日下午,百度于北京总部发布了新一代大语言模型、生成式AI产品文心一言。首批用户即可通过邀请测试码,在文心一言官网体验产品,后续将陆续开放给更多用户。邀请码申请方式请往下看。百度文心官网地址:https://..

发表回复

返回顶部