首先想到的是c语言中的 malloc 和 free ,这两个是库函数,而在c++中 new 和 delete是运算符(和 + - * /一样),在使用new的时可以看成使用默认使用构造函数+malloc,使用delete时可以看成使用析构函数+free。
- 简单说一下malloc
系统管理堆内存运用的是链表的方式:分配堆内存地址时依次由低向高遍历”堆链“,但遇到大小合适的堆块时,将这个堆块从”堆链“中删除,其他的继续构成新的链。当这一块的内存分配完后还有剩余,则将剩余的继续添加到”堆链“里。
分配空间时,不仅会分配到程序员所需空间的大小,系统还会分配一个头”Header“
typedef long Align;/*按照long类型的边界对齐*/
union header/*块的头部*/
{
struct
{
union header *ptr;/*空闲块链表中的下一块*/
unsigned size;/*本块的大小*/
}s;
Align x;/*强制块对齐*
};
可以说这个Header中保存了一个所申请空间的大小,而系统返回的Header是不能被用户访问的。
- 接下来是正文
情况1
int* a = new int[10];
delete a;
delete[] a;
第一种方式一定会造成内存泄漏吗?
其实两种释放内存的方式效果相同,原因在于:分配简单类型内存时,内存大小已经确定,系统可以记忆并且进行管理,在析构时,系统并不会调用析构函数。
(它直接通过指针可以获取实际分配的内存空间,在分配过程中系统会记录分配内存的大小等信息,此信息保存在结构体CrtMemBlockHeader中,具体情况可参看VC安装目录下CRT/SRC/DBGDEL.cpp)
- 情况2
如果是类class,则情况会出现不同
class A
{
public:
A()
{
m = new int[10];
}
~A()
{
cout<<"~A"<<endl;
}
private:
char* m;
};
int main()
{
A* ptr = new A[3];
//ptr = ptr - 1;//注1
delete ptr;
return 0;
}
这个程序运行后会出现这个错误
这个错误的意思是程序在结束后系统会调用析构函数发现我们在上面malloc中提出的Header被错误的更改了。
假设用户接受到的指针返回值是address,因为在使用delete[]时,会默认从address-4开始,而不是addrss。(4是因为malloc结构体中的类型为long,而且在32位系统下。这里减一是一个单元格,也就4)。而这个结构体中存储的size是将要释放的大小。
这个是加上那个引用后的运行结果,可以看到它只析构了一次。
所以1. delete a 仅释放了a指针指向的全部内存空间,但是只调用了a[0]的析构函数,剩下的a[1]和a[2]中m申请的内存不能够释放,则造成了内存泄漏。
2. delete[] a 释放了a指针申请的内存并且调用析构函数释放掉所有申请的内存
总结一下:假设 ptr 代表new返回的内存空间地址
delete ptr 代表释放ptr指向内存,并且只能用来释放内存。
delete[] ptr 代表释放ptr指向的内存,并且逐个调用对象的析构函数。
对于像int/char/long/int*/struct等简单数据类型,由于没有析构函数,所以delete和delete[] 是一样!但是c++中的对象数组就不同了!
如果对象中使用了外部的资源(或操作系统资源)例如:socket,file,thread等,这些资源都是有限的,端口号最大是65535,如果不被释放,在没有端口可以用的时候,连网都上不去!
所以,在生成对象数组时,一定要记得用delete[]!