内存管理(二)new[] 使用 delete 造成内存泄漏

正文

在说明使用new[]申请内存,而使用delete释放的问题之前,先了解new[] 和 delete[] 的工作方式。

一.new[]

1.调用operator new[] ,由operator new[]调用n次operator new 来分配n个对象的空间。
2.调用n次构造函数。

二.delete[]

1.调用n次析构函数。
2.调用operator delete[] ,由operator delete[]调operator delete来释放空间。
先探究一下是否真的是这样的:

#include<stdio.h>
#include<stdlib.h>
class A
{ 
public:
 int a;
 ~A()
 { 
  printf("A析构\n");
 }
};
void operator delete(void* p)
{ 
 printf("释放内存\n");
 free(p);
}int main()
{ 
 A* p = new A[10];
 delete[] p;
}

运行结果:
A析构
A析构
A析构
A析构
A析构
A析构
A析构
A析构
A析构
A析构
释放内存

我们重载了operator delete 后,可以看见,先执行了10次析构,然后释放了一次空间。
这样 new[] 搭配 delete[] 使用的方式是完全正确且没有问题的。

三.简单数据类型的new[] delete

void operator delete(void* p)
{ 
 printf("释放内存\n");
 free(p);
}int 
int main()
{ 
 int *p =  new inr[10];
 delete p;
}

输出结果: 释放内存
可以看到对于简单数据类型,使用new[] 和 delete 并没有问题(实则也没有造成内存泄漏)。

四.复杂数据类型的new[] delete

对于类内部没有自己定义析构函数的类型

#include<stdio.h>
#include<stdlib.h>
class A
{ 
public:
 int a;
};
void operator delete(void* p)
{ 
 printf("释放内存\n");
 free(p);
}int main()
{ 
 A* p = new A[10];
 delete p;
}

运行结果: 释放内存

程序正常运行了,且空间正常释放。所以对于没有自己定义析构函数的数据类型也是可以的。

对于类内部自己定义析构函数的类型

#include<stdio.h>
#include<stdlib.h>
class A
{ 
public:
 int a;
 ~A()
 { }
};
void operator delete(void* p)
{ 
 printf("释放内存\n");
 free(p);
}int main()
{ 
 A* p = new A[10];
 delete p;
}

这样的代码是不可以运行的,会抛出异常。那么为什么会这样?
上文说到的,delete[]做的两个工作,调用n次析构,调用operator delete 释放空间。编译器在调用析构的工作上,做出了优化,有些数据类型的析构函数是系统合成的没有作用的析构函数(trivial destructor),对于这样的情况是没必要调用多次析构的,所以系统决定什么也不做,这样就只用释放空间就可以了。同样,简单数据类型也是这样的。
对于空间是否被完全释放的问题主要取决于类的内部是否由指针需要被释放,如果没有,就不存在内存泄漏的问题。因为我们在分配内存的时候,其实会有两个cookie来存放这一次所申请内存的空间大小,所以无论是delete[] 还是 delete 都会完整的把申请的内存释放掉。但是如果,其中含有指针,我们一般会在析构函数中释放掉指针所指向的内存空间,可是使用delete只会调用一次析构,也就只会释放一次指针,其余指针所指向的内存就会泄漏。那么编译器要求,对于内部含有nontrivial destructor的类型,在释放时必须要每一个都得调用到它的析构函数,这样确保了每个对象内部的指针所指向的内存都会被释放。

以下方式会造成内存泄漏:

#include<stdio.h>
#include<stdlib.h>
class A
{ 
public:
 int* a;
 A()
 { 
  a = new int;
 }
};
void operator delete(void* p)
{ 
 printf("释放内存\n");
 free(p);
}int main()
{ 
 A* p = new A[10];
 delete p;
}

运行结果:释放内存

其中泄漏的内存就是每个对象中指针所指向的内存。

总结:
造成内存泄漏的真正原因是因为使用delete只会执行一次析构函数,倘若类内部有要在析构函数中释放,就会造成没有执行析构函数的对象内部指针所指向空间没有被回收。

    原文作者:说LAI话长
    原文地址: https://blog.csdn.net/weixin_45074185/article/details/107146774
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞