C++——new和delete关键字

什么是new和delete

newdelete不是函数,和sizeof一样都是C++定义的关键字,不同的是sizeof在编译时就可以确定其返回值,而newdelete相对复杂

示例

string *ps = new string("hello world");

如果换做c语言,上面这句话就会变成:

char *ps = (char *)malloc(sizeof(char)*12);
ps = "hello world";

这里就可以看出newmallocc的几点不同:

  • malloc申请完空间后不会对内存进行必要的初始化,而new 可以
  • new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,即new类型安全性的操作符;而malloc内存分配成功后返回的是void*,需要通过强制类型转换,将通用类型指针void*转换成所需要的指针
  • new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算;而malloc需要显示地写出所需内存块的大小

new和malloc更多的不同请参考此文章

总结表

特征new/deletemalloc/free
分配内存的位置自由存储区
内存分配成功的返回值完整类型指针void*
内存分配失败的返回值默认抛出异常返回NULL
分配内存的大小由编译器根据类型计算得出必须显式指定字节数
处理数组有处理数组的new版本new[]需要用户计算数组的大小后进行内存分配
已分配内存的扩充无法直观地处理使用realloc简单完成
是否相互调用可以,看具体的operator new/delete实现不可调用new
分配内存时内存不足客户能够指定处理函数或重新制定分配器无法通过用户代码进行处理
函数重载允许不允许
构造函数与析构函数调用不调用

new和delete的背后机制

通过示例说明:

class A
{
  private:
    int var;
    FILE *file;
   public:
   	A(int v): var(v) {
        fopen_s(&file, "test", "r");
    }
    ~A(){
        fclose(file);
    }
};

类A中有两个私有成员,一个构造函数和析构函数,构造函数根据传递参数初始化var并且打开文件,析构函数关闭文件

我们使用下面代码创建一个类的对象,返回其指针pa

A *pa = new A(10);

如下图所示new完成的工作:

在这里插入图片描述

可以将new实例化对象的过程分为三步:

  1. 分配指定大小的内存块;
  2. 在内存块上调用构造函数对类对象进行初始化
  3. 返回内存块的地址(指针)

那么delete会做什么呢?

delete pa;

如下图所示:

即将delete一个对象的过程也可以分两步:

  1. 先调用析构函数,将打开的文件关闭
  2. 释放pa所指内存块的空间,即pa变成空指针

申请和释放一个数组

常用的动态分配一个数组方法

string *psa = new string[10];
int *pia = new int[10];

上面在申请数组的时候都用到了new []表达式,第一个数组是string类型,在分配了保存对象的内存空间(10个string的大小),并调用string类的默认构造函数来依次初始化每个元素,最后返回第一个string的地址作为string数组的地址;第二个数组是int类型的,int是内置类型不存在构造函数,所以new的过程中,不存在初始化,只分配了10个int类型的内存空间。

如果想释放空间,则使用下面语句

delete [] psa;
delete [] pia;

都用到了delete []表达式,注意这个[]一般情况下不能漏下。释放string数组的空间时,先对数组内的每个元素都调用析构函数析构对象,再释放掉整个数组的空间;而在释放int数组时,因为不存在析构函数,所以会直接释放整个int数组的空间。

可以看到delete[]中并没有填数组的大小,那么delete关键字怎么知道需要调用析构函数多少次呢?

回到new [size],我们new一个对象数组时,还需要保存数组的维度,c++的做法是在分配数组空间时多分配4个字节,专门保存数组的大小,在delete []时就可以取出这个保存的数,就知道需要调用析构函数多少次了。

依旧以类A为例,

A *pAa = new A[3];

发生过程如下图:

在这里插入图片描述

注意到,在申请数组对象的上面确实多分配了4个字节用来保存数组的大小,但是最终返回的地址(指针)是指向第一个数组元素的。

在释放空间时:

delete []pAa;

发生的过程如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Oq1fz5ju-1669642247943)(D:\Note\截图\image-20221128211757009.png)]

要注意的是,先从目标地址的前4个字节中,取出数作为调用析构函数的次数,依次析构数组内的元素;最后在释放内存空间的时候,传递给operator delete[]()的参数是pAa-4,即还要释放前面4个字节

new/delete与new[]/delete[]的配对使用

经过上面的分别对new/delete和new[]/delete[]的使用,可以得知这两对之间一般情况下不能拆开随意组合,不然会导致严重的内存泄露/重复释放问题

string *psa = new string[10];
delete psa;

如果delete没有后面的[]意味它只会析构一次,那么剩下的9个string对象和上面的4字节数将永远不会被释放,当数组很大时会造成很严重的内存泄露;相反如果是new/delete[]会导致重复释放内存的问题。

一般情况下意味也有特殊情况,如下所示:

int *pia = new int[10];
delete pia;

这个操作又是合理的,因为差别在int和string不同,int是内置类型,不存在构造和析构函数,也就是说new[]的时候多分配的4个字节,是因为delete的时候需要知道调用析构函数的次数,但是当对象类型都没有析构函数时,也就没有多分配这4个字节的必要。直接delete piaoperator delete传递的参数就是pia的值(数组第一个元素的地址),直接释放所分配的内存块大小即可,无需析构。

参考文章
浅谈 C++ 中的 new/delete 和 new[]/delete[]

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

linengcs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值