new和malloc的基本区别
执行 new 实际上执行三个过程:
- 第一步: 调用operator new函数,(对于数组是operator new[])分配一块足够大的,原始的,未命名的内存空间以便存储特定类型的对象。
- 第二步: 编译调用对应的构造函数以构造对象, 并传入初值
- 第三步: 返回指向该对象的指针。如果在第⼀步分配空间中出现问题,则抛出 std::bad_alloc 异常,或被某个设定的异常处理函数捕获处理;如果在第⼆步构造对象时出现异常,则自动调用 delete 释放内存。
补充:执行 delete 实际上也有两个过程:
- 使⽤析构函数对对象进行析构;2. 编译器调用 operator delete 函数释放内存空间
可以看出new和malloc的基本区别:
new得到的是经过初始化的空间, 而malloc得到的是未初始化的空间;
delete 和 free 同理, delete 同时完成释放空间和析构对象, 而 free 仅仅是释放空间
细分new与malloc的区别
1> 申请的内存所在位置不同
new 操作符是从自由存储区(free store)上为对象动态分配内存空间, 而malloc函数是从堆上动态分配内存. 自由存储区是C++基于new操作符引入的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区. 堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配
2> 返回类型安全性
new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void ,需要通过强制类型转换将void指针转换成我们需要的类型。
类型安全很大程度上可以等价于内存安全,类型安全的代码不会试图访问自己没被授权的内存区域。
3> 是否能够直观地重新分配内存
realloc的支持
使用malloc分配的内存后,如果在使用过程中发现内存不足,可以使用 realloc 函数进行内存重新分配实现内存的扩充。
realloc先判断当前的指针所指内存是否有足够的连续空间,如果有,原地扩大可分配的内存地址,并且返回原来的地址指针;如果空间不够,先按照新指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来的内存区域。
new不支持
new没有这样直观的配套设施来扩充内存。
4> 是否需要指定内存大小
使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算,而malloc则需要显式地指出所需内存的尺寸。
5> 是否可以被重载
operator new /operator delete可以被重载.
我们有足够的自由去重载operator new /operator delete ,以决定我们的new与delete如何为对象分配内存,如何回收对象.
而malloc/free并不允许重载.
举例
例如将 new 运算符重载为 private 属性. 则类只能静态分配.
静态分配: 静态建立一个类对象, 由编译器为对象在栈空间中分配内存;
6> new与malloc是否可以相互调用
operator new /operator delete的实现可以基于malloc,而malloc的实现不可以去调用new。下面是编写operator new /operator delete 的一种简单方式,其他版本也与之类似:
void operator new (sieze_t size)
{
if(void mem = malloc(size))
return mem;
else
throw bad_alloc();
}
void operator delete(void *mem) noexcept
{
free(mem);
}
7> 分配内存不足时的处理
在operator new抛出异常以反映一个未获得满足的需求之前,它会先调用一个用户指定的错误处理函数,这就是new-handler。new_handler是一个指针类型:
namespace std
{
typedef void (*new_handler)();
}
指向了一个没有参数没有返回值的函数,即为错误处理函数。为了指定错误处理函数,客户需要调用set_new_handler,这是一个声明于的一个标准库函数:
namespace std
{
new_handler set_new_handler(new_handler p ) throw();
}
set_new_handler的参数为new_handler指针,指向了operator new 无法分配足够内存时该调用的函数。其返回值也是个指针,指向set_new_handler被调用前正在执行(但马上就要发生替换)的那个new_handler函数。
对于malloc,客户并不能够去编程决定内存不足以分配时要干什么事,只能看着malloc返回NULL。
连接里面的handle函数是自定义的异常处理函数。
深入解析new、operator new、::new、placement new
参考:new和malloc区别和联系
2.1 追问: new是否能在堆上动态分配内存?
这取决于operator new 的实现细节。自由存储区不仅可以是堆,还可以是静态存储区,这都看operator new在哪里为对象分配内存。
2.2 追问: new operator 和 operator new的区别?
new就是new operator,调用new的时候编译器做了三件事,
1.是用operator new( )分配内存,
2.是调用构造函数(就是你new的类类型或者string等类型的构造函数)。
3.返回相应的指针。
new的底层是调用operator new( )分配内存的。该函数调用malloc申请内存。
主要区别就是: new operator是c++内建的,无法改变其行为;而 operator new 是可以根据自己的内存分配策略去重载的。
参考链接:C++ operator new和new operator区别.
2.3 追问: 有了解placement new吗?
在使用new关键字建立一个新的对象的时候,在编译器的第二步就是调用对象的构造函数生成类对象。这一步使用的就是placement new来实现的,即在取得了一块可以容纳指定类型对象的内存之后,在这块内存上构造一个对象。
什么情况下使用 placement new?
使用new操作符分配内存需要在堆中查找足够大的剩余空间,这个操作速度是很慢的,而且有可能出现无法分配内存的异常(空间不够)。placement new就可以解决这个问题。我们构造对象都是在一个预先准备好了的内存缓冲区中进行,不需要查找内存,内存分配的时间是常数;而且不会出现在程序运行中途出现内存不足的异常。所以,placement new非常适合那些对时间要求比较高,长时间运行不希望被打断的应用程序
参考链接:C++中new、operator new和placement new的区别
2.4 追问: 为什么有了malloc/free还需要new/delete?
因为对于⾮内部数据类型⽽⾔,光⽤ malloc /free ⽆法满⾜动态对象的要求。对象在创建的同时需要⾃动执⾏构造函数,对象在消亡以前 要⾃动执⾏析构函数。
由于 malloc/free 是库函数⽽不是运算符,不在编译器控制权限之内, 不能够把执行的构造函数和析构函数的任务强加于 malloc/free,所以有了 new/delete 操作符
