和很多人一样,我一直觉得new/delete和malloc/free的用法很随意,直到我真正遇到了麻烦,才想着去好好区分一下。
(1)首先mallo函数原型void* malloc(size_t)。头文件stdlib.h。malloc 向系统申请分配指定size_t个字节的内存空间。返回类型是 void* 类型。void* 表示未确定类型的指针。C,C++规定,void* 类型可以强制转换为任何其它类型的指针。
int* p = (int *) malloc (sizeof(int)); 必须加(int* )强制转换,否则编译会报错。
(2)那么malloc是怎么工作的呢。malloc首先会将内存分成一个长的空闲链表,当你申请空间时,它会沿着链表找到一块足以满足要求的内存,将需要的大小分配给你,剩余的返回空余链表中,当你使用free函数释放空间时,它将这部分空间返回到链表中。这样有时候就会导致,空闲链会被切成很多的小内存片段,如果这时你申请一个大的内存片段,那么空闲链上可能没有可以满足要求的片段了。于是,malloc函数请求延时,并开始在空闲链上翻箱倒柜地检查各内存片段,对它们进行整理,将相邻的小空闲块合并成较大的内存块。如果无法获得符合要求的内存块,malloc函数会返回NULL指针,因此在调用malloc动态申请内存块时,一定要进行返回值的判断。(这一点好像大多数程序都不会注意)
if(p)
cout<<"Memory Allocated ";
else
cout<<"Not Enough Memory!";
(3)至于new到底干了些什么,我们先不追究,可以看看它和malloc的区别:
①申请的内存所在位置
new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区。而堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配,C语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。
②返回类型的区别
new分配成功时,返回的是对象类型的指针,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。
③内存分配失败时的返回值
new内存分配失败时,会抛出bac_alloc异常,它不会返回NULL,所以它不用额外去进行返回值判断,因为即便失败,也不会返回NULL;malloc分配内存失败时返回NULL。
④是否需要指定内存大小
使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算,而malloc则需要显式地指出所需内存的尺寸。
⑤操作对象不同
malloc与free是C++/C 语言的标准库函数,new/delete 是C++的运算符。对于非内部数据类的对象而言,光用maloc/free 无法满足动态对象的要求。(比如string类型就只能用new,因为它不是内部数据类)对象在创建的同时要自动执行构造函数, 对象消亡之前要自动执行析构函数。由于malloc/free 是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加malloc/free。
⑤是否调用构造函数和析构函数
使用new操作符来分配对象内存时会经历三个步骤:
- 第一步:调用operator new 函数(对于数组是operator new[])分配一块足够大的,原始的,未命名的内存空间以便存储特定类型的对象。
- 第二步:编译器运行相应的构造函数以构造对象,并为其传入初值。
- 第三步:对象构造完成后,返回一个指向该对象的指针。
使用delete操作符来释放对象内存时会经历两个步骤:
- 第一步:调用对象的析构函数。
- 第二步:编译器调用operator delete(或operator delete[])函数释放内存空间。
⑥对数组的处理
C++提供了new[]与delete[]来专门处理数组类型:它会分别调用构造函数函数初始化每一个数组元素,释放对象时为每个对象调用析构函数。
A * ptr = new A[10];//分配10个A对象
delete [] ptr;
至于malloc,它并知道你在这块内存上要放的是不是数组,反正它就给你一块原始的内存,再给你个内存的地址就完事。所以如果要动态分配一个数组的内存,还需要我们手动自定数组的大小。
⑦new与malloc是否可以相互调用
operator new /operator delete的实现可以基于malloc,而malloc的实现不可以去调用new。
如果用free释放“new创建的动态对象”,那么该对象因无法执行析构函数而可能导致程序出错。如果用delete释放“malloc申请的动态内存”,理论上讲程序不会出错,但是该程序的可读性很差。
⑧是否可以被重载
opeartor new /operator delete可以被重载。标准库是定义了operator new函数和operator delete函数的8个重载版本,总之,我们知道我们有足够的自由去重载operator new /operator delete ,以决定我们的new与delete如何为对象分配内存,如何回收对象。
而malloc/free并不允许重载。
⑨ 能够直观地重新分配内存
使用malloc分配的内存后,如果在使用过程中发现内存不足,可以使用realloc函数进行内存重新分配实现内存的扩充。realloc先判断当前的指针所指内存是否有足够的连续空间,如果有,原地扩大可分配的内存地址,并且返回原来的地址指针;如果空间不够,先按照新指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来的内存区域。
new没有这样直观的配套设施来扩充内存。