写这个问题的原因是面试时被问到new和malloc申请的内存有何区别,当时没有回答出来,回来后查阅了很多的博客,发现自己不会的真的特别多,所以现在自己写了一个回答!!!
首先我们大概知道C和C++申请动态内存的方式如下:
C:使用malloc、calloc、realloc函数申请动态内存,使用free函数释放申请到的内存。申请内存空间的头文件是:#include <malloc.h>.
C++:使用new操作符申请动态内存,使用delete释放申请到的内存。
然后,接下来现在我们来分析一下 new 和 malloc 的不同之处:
1、申请内存所在的位置
. new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。
自由存储是C++中通过 new 和 delete 动态分配和释放对象的抽象概念,通过 new 来申请的内存区域可称为自由存储区;堆(heap)是C语言和操作系统的术语,堆是操作系统所维护的一块特殊内存,它提供了动态分配的功能,当运行程序调用 malloc() 时就会从中分配,之后调用 free() 时会把内存交还。
问:自由存储区与堆是两块不同的内存区域吗?它们有可能相同吗?
我在网上看了很多的博客,他们都说划分自由存储区与堆的分界线就是new/delete与malloc/free。然而,尽管C++标准没有要求,但很多编译器的new/delete都是以malloc/free为基础来实现的。那么借以malloc实现的new,所申请的内存到底是在堆上还是在自由存储区上?
其实:基本上,所有的C++编译器默认使用堆来实现自由存储,也即是缺省的全局运算符new和delete也许会按照malloc和free的方式来被实现,这时藉由new运算符分配的对象,说它在堆上也对,说它在自由存储区上也正确。但程序员也可以通过重载操作符,改用其他内存来实现自由存储,例如全局变量做的对象池,这时自由存储区就区别于堆了。我们所需要记住的就是:堆是操作系统维护的一块内存,而自由存储区是C++中通过new与delete动态分配和释放对象的抽象概念。堆与自由存储区并不等价。
2、返回类型的安全性
. new操作符内存分配成功时,返回的是对象类型的指针,类型要求严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回 void * ,需要通过强制类型转换将 void* 指针转换成我们需要的类型,故malloc不是符合类型安全性的操作符。
3、是否需要指定所申请内存的大小
. 使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算,而malloc则需要显式地指出所需内存的尺寸。
eg:
A* dd = new A;
A* dd = (A*) malloc (sizeof(A));
4、内存分配失败时的返回值
. new内存分配失败时,抛出bad_alloc异常来报告分配失败,它不会返回NULL;malloc分配内存失败时会返回NULL。
5、是否调用构造函数/析构函数
. 使用new操作符来分配对象内存时会经历三个步骤:
**第一步:**调用operator new 函数(对于数组是operator new[])分配一块足够大的,原始的,未命名的内存空间以便存储特定类型的对象。
**第二步:**编译器运行相应的构造函数以构造对象,并为其传入初值。
**第三步:**对象构造完成后,返回一个指向该对象的指针。
使用delete操作符来释放对象内存时会经历两个步骤:
**第一步:**调用对象的析构函数。
**第二步:**编译器调用operator delete(或operator delete[])函数释放内存空间。
总之来说,new/delete会调用对象的构造函数/析构函数以完成对象的构造/析构。而malloc则不会。
6、内存不足时的处理方式
. 使用malloc分配的内存,如果在使用过程中发现内存不足,可以使用realloc函数进行内存重新分配实现内存的扩充。realloc会先判断当前的指针所指向的内存是否有足够的连续空间,如果有,原地扩大可分配的内存地址,并且返回原来的地址指针;如果空间不够,先按照新指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来的内存区域。而对于new没有这样扩充内存的处理操作
7、内存不足时的返回值
. 对于new:在operator new抛出异常以反映一个未获得满足的需求之前,它会先调用一个用户指定的错误处理函数—new-handler。new_handler是一个指针类型:指向了一个没有参数没有返回值的函数,即为错误处理函数。为了指定错误处理函数,客户需要调用set_new_handler,这是一个声明了的一个标准库函数:set_new_handler的参数为new_handler指针,指向了operator new 无法分配足够内存时该调用的函数。其返回值也是个指针,指向set_new_handler被调用前正在执行(但马上就要发生替换)的那个new_handler函数。对于malloc,客户并不能够去编程决定内存不足以分配时要干什么事,只能看着malloc返回NULL。
知识补充:
C++ 内存分配(new,operator new)
一 、new 运算符和 operator new():
new:指我们在C++里通常用到的运算符,比如A* a = new A; 对于new来说,有new和::new之分。
operator new():指对new的重载形式,它是一个函数,并不是运算符。对于operator new来说,分为全局重载和类重载,全局重载是void* ::operator new(size_t size),在类中重载形式 void* A::operator new(size_t size)。需要注意的是operator new()完成的操作一般只是分配内存,事实上系统默认的全局::operator new(size_t size)也只是调用malloc分配内存,并且返回一个void*指针。而构造函数的调用(如果需要)是在new运算符中完成的。
我在前面已经讲了new操作符来分配对象内存时经历的三个步骤,这里我们仅讨论前两步:1.分配内存,2.调用A()构造对象。
事实上,分配内存这一操作就是由operator new(size_t)来完成的,如果类A重载了operator new,那么将调用A::operator new(size_t ),如果没有重载,就调用::operator new(size_t ),全局new操作符由C++默认提供。因此前面的两步也可以解释为:1.调用operator new ,2.调用构造函数。
暂时写不下去了,刚做了一个笔试题,大题GG了,难受,博客有时间我再继续补充,找工作好难,共勉~