array new 一定要搭配 array delete

发布于 2020-08-14  78 次阅读


学习侯捷老师视频,看到arrry new和array delete部分,有所体会,记录一下。

new和delete

相较于C语言用库函数 malloc 和 free 函数来分配和撤销内存,C++提供了较简便而功能较强的“运算符” new 和 delete 来取代 malloc 和 free 函数。很多网络博客写因为new 和 delete 是运算符,不是函数,因此执行效率高。

然而 new 和 delete 的底层实则本就是是调用的 malloc 和 free 函数,然后再为其调用构造函数和析构函数。

老师ppt中使用的是一个复数类举例,类中只有两个double值。

此时new会被编译成下面三个步骤:

  • 调用operator new函数申请内存,operator new其实就是调用的malloc函数。

的撒

  • 指针强制类型转换(老师没有细讲,学到后再来补充)。
  • 调用类的构造函数,在申请的内存处构造对象。


delete则会被编译成下面两个步骤:

  • 首先调用对象的析构函数。
  • 调用operator delete释放内存,operator内部调用的就是free函数。

动态分配所得的内存块

进行后续的讨论前,先让我们看一下动态分配所得的内存块是什么样子的。

在malloc为用户分配内存的时候,除了分配用户本身的内存,还会在内存前后加上两个cookie,来记录分配了多少内存,这样在调用free函数的时候才能准确的回收内存。也就是图中橙红色部分。

内存的分配是16的倍数,但为了记录此处内存是否被释放,将最后一位视作标识符,1则为未释放,64的16进制为40,所以cookie数值为41。

灰色的部分是Debugger Header,占(32+4)bytes。

蓝色部分是填补区pad。

动态分配所得的array

此时在数据域前多出来了4个bytes存储数组大小,其余与上文无异。

此时,假若我们使用array new生成了complex数组,然后调用delete去释放,先对第一个复数进行析构,然后不会对第二第三个进行析构,但是最后free会把cookie之间的内存全部释放掉,看起来成功的完成了new array与delete array,然而,真的是如此吗?

实则不然,当我们换成带指针的类时,就会发生内存泄漏。

array new 一定要搭配array delete

此时,与复数类一致,使用array new生成了string数组,然后调用delete去释放,先对第一个string*进行析构,释放掉其指向的空间,不会对第二第三个进行析构。然后free会把cookie之间的内存全部释放掉,怎么样?释放成功了吗?

当然没有,成功的只有第一个string,第二第三个string*指向的空间发生了内存泄漏。

而当使用的是delete array时,读取到数据域前的对象数量标识,就会执行相应次数的delete,则不会发生内存泄漏。

总结

在调用new来分配数组时(new array),会多申请4个字节(与编译器有关)来存储数组大小,以供编译器知悉在调用delete array的时候需要调用几次析构函数之后再释放内存,避免内存泄漏,所以二者一定要搭配使用。