20170816_new和malloc的区别
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/cmm0401/article/details/77206594 </div>
<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-cd6c485e8b.css">
<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-cd6c485e8b.css">
<div class="htmledit_views" id="content_views">
20170816_new和malloc的区别
版本1:
1、操作对象有所不同:
malloc 与free 是C++/C 语言的标准库函数,new 与delete 是C++ 的运算符。
对于非内部数据类的对象而言,光用maloc 与free 无法满足动态对象的需求。对象在创建的同时要自动执行构造函数, 对象消亡之前要自动执行析构函数。但是,由于malloc 与free 是库函数而不是运算符,它们不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加给malloc 与free。
2、用法上有所不同:
(1)malloc 和free 函数原型:
一、函数malloc 的原型如下: void * malloc(size_t size);
用malloc 申请一块长度为 length 的整数类型的内存,程序如下:
int *p = (int *) malloc (sizeof(int) * length);
我们应当把注意力集中在两个要素上:“类型转换”和“sizeof()”。
1)malloc 函数的返回值的类型是 void *,所以在调用malloc 函数时要显式地进行类型转换,将void * 转换成所需要的指针类型。
2)malloc 函数本身并不识别要申请的内存是什么类型,它只关心内存的总字节数 length。
二、函数free 的原型如下: void free( void * memblock );
为什么free 函数不象malloc 函数那样复杂呢?这是因为指针 p 的类型以及它所指向的内存容量事先都是知道的,语句 free(p) 能正确地释放内存。
如果p 是NULL 指针,那么free 对p 无论操作多少次都不会出问题。但是,如果p 不是NULL 指针,那么free 对p 连续操作两次就会导致程序运行错误。为什么呢?
原因是:
1)当指针 p=NULL 时,指针p 是无效指针,所以free(p); 操作也是无效的,操作多少次也是无效,就相当于不执行free(p);。
2)而当p 不为NULL 时,一般又有两种情况:
- 一种是p 是野指针(野指针:未被初始化的指针),一次都不能操作,操作就要产生运行时错误;
- 二种是p 是由malloc 函数分配的有效空间的首地址,只能用free(p); 释放一次,然后p 就变成了所谓“悬浮指针”(悬浮指针:当所指向的对象被释放或者收回,但是对该指针的指向并没有作任何的修改,以至于该指针仍然指向已经被回收的内存地址,此情况下该指针称为悬浮指针),就是说它指向的内存空间已经不存在了,这时若还进行free(p); 操作,也将产生运行时错误,就是说已经没有空间可释放了,代码运行时出错了!
- 所以一般主张 free(p); 操作成功后应立马写一句 p=NULL; 以规避这种错误。
(2)new 和delete 的使用要点:
1)new 运算符使用起来要比malloc函数简单得多,例如:
int *p1 = (int *) malloc (sizeof(int) * length);
int *p2 = new int[length];
这是因为new 内置了sizeof()函数、类型转换和类型安全检查功能。对于非内部数据类型的对象而言,new 在创建动态对象的同时完成了初始化工作。如果对象有多个构造函数,那么new 的语句也可以有多种形式。
如果用new 创建对象数组,那么只能使用对象的无参数构造函数。例如
Obj *objects = new Obj[100]; // 创建100 个动态对象
不能写成:
Obj *objects = new Obj[100](1); // 创建100 个动态对象的同时赋初值1
在用delete 释放对象数组时,留意不要丢了符号‘[]’。例如
delete [] objects; // 正确的用法
delete objects; // 错误的用法,这种方法相当于 delete objects[0],漏掉了另外99 个对象。
3、前面两部分的小型总结:
1) new 自动计算需要分配的空间,而malloc 需要手工计算字节数。(使用sizeof()运算函数)
2) new 是类型安全的,而malloc 不是,比如:
int *p = new float[2]; // 编译时指出错误(前后类型不一致)
int *p = malloc(sizeof(float) * 2); // 编译时无法指出错误
3) new operator 由两步构成,分别是 operator new 和 construct:
- operator new 对应于malloc,但operator new 可以重载,可以自定义内存分配策略,甚至不做内存分配,甚至分配到非内存设备上。而malloc 无能为力。
- new 将调用constructor(构造函数),而malloc 不能;delete 将调用destructor(析构函数),而free 不能。
- malloc/free 要库文件支持,new/delete 则不要。
4)既然new 与delete 的功能完全覆盖了malloc 与free,为什么C++ 还要保留malloc 与free 呢?
因为C++ 程序经常要调用C 函数,而C 程序只能用malloc 与free 管理动态内存。
如果用free 释放“new 创建的动态对象”,那么该对象因无法执行析构函数而可能导致程序出错。
如果用delete 释放“malloc 申请的动态内存”,理论上讲程序不会出错,但是该程序的可读性很差。
所以运算符 new 与delete、库函数 malloc 与free 必须配对使用!
***************************************************************************************************************
***************************************************************************************************************
版本2:
new与malloc的10点区别(转自:http://blog.jobbole.com/102002/)
1. 申请的内存所在位置
new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区。而堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配,C语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。
那么自由存储区是否能够是堆(问题等价于new是否能在堆上动态分配内存),这取决于operator new 的实现细节。自由存储区不仅可以是堆,还可以是静态存储区,这都看operator new在哪里为对象分配内存。
特别的,new甚至可以不为对象分配内存!定位new的功能可以办到这一点:
1
|
new
(
place_address
)
type
|
place_address为一个指针,代表一块内存的地址。当使用上面这种仅以一个地址调用new操作符时,new操作符调用特殊的operator new,也就是下面这个版本:
1
|
void
*
operator
new
(
size_t
,
void
*
)
//不允许重定义这个版本的operator new
|
这个operator new不分配任何的内存,它只是简单地返回指针实参,然后右new表达式负责在place_address指定的地址进行对象的初始化工作。
2.返回类型安全性
new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。
类型安全很大程度上可以等价于内存安全,类型安全的代码不会试图方法自己没被授权的内存区域。关于C++的类型安全性可说的又有很多了。
3.内存分配失败时的返回值
new内存分配失败时,会抛出bac_alloc异常,它不会返回NULL;malloc分配内存失败时返回NULL。
在使用C语言时,我们习惯在malloc分配内存后判断分配是否成功:
1
2
3
4
5
6
7
8
9
|
int
*
a
=
(
int
*
)
malloc
(
sizeof
(
int
)
)
;
if
(
NULL
==
a
)
{
.
.
.
}
else
{
.
.
.
}
|
从C语言走入C++阵营的新手可能会把这个习惯带入C++:
1
2
3
4
5
6
7
8
9
|
int
*
a
=
new
int
(
)
;
if
(
NULL
==
a
)
{
.
.
.
}
else
{
.
.
.
}
|
实际上这样做一点意义也没有,因为new根本不会返回NULL,而且程序能够执行到if语句已经说明内存分配成功了,如果失败早就抛异常了。正确的做法应该是使用异常机制:
1
2
3
4
5
6
7
8
|
try
{
int
*
a
=
new
int
(
)
;
}
catch
(
bad_alloc
)
{
.
.
.
}
|
如果你想顺便了解下异常基础,可以看http://www.cnblogs.com/QG-whz/p/5136883.htmlC++ 异常机制分析。
4.是否需要指定内存大小
使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算,而malloc则需要显式地指出所需内存的尺寸。
1
2
3
|
class
A
{
.
.
.
}
A
*
ptr
=
new
A
;
A
*
ptr
=
(
A
*
)
malloc
(
sizeof
(
A
)
)
;
//需要显式指定所需内存大小sizeof(A);
|
当然了,我这里使用malloc来为我们自定义类型分配内存是不怎么合适的,请看下一条。
5.是否调用构造函数/析构函数
使用new操作符来分配对象内存时会经历三个步骤:
- 第一步:调用operator new 函数(对于数组是operator new[])分配一块足够大的,原始的,未命名的内存空间以便存储特定类型的对象。
- 第二步:编译器运行相应的构造函数以构造对象,并为其传入初值。
- 第三部:对象构造完成后,返回一个指向该对象的指针。
使用delete操作符来释放对象内存时会经历两个步骤:
- 第一步:调用对象的析构函数。
- 第二步:编译器调用operator delete(或operator delete[])函数释放内存空间。
总之来说,new/delete会调用对象的构造函数/析构函数以完成对象的构造/析构。而malloc则不会。如果你不嫌啰嗦可以看下我的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class
A
{
public
:
A
(
)
:
a
(
1
)
,
b
(
1.11
)
{
}
private
:
int
a
;
double
b
;
}
;
int
main
(
)
{
A
*
ptr
=
(
A
*
)
malloc
(
sizeof
(
A
)
)
;
return
0
;
}
|
在return处设置断点,观看ptr所指内存的内容:
可以看出A的默认构造函数并没有被调用,因为数据成员a,b的值并没有得到初始化,这也是上面我为什么说使用malloc/free来处理C++的自定义类型不合适,其实不止自定义类型,标准库中凡是需要构造/析构的类型通通不合适。
而使用new来分配对象时:
1
2
3
4
|
int
main
(
)
{
A
*
ptr
=
new
A
;
}
|
查看程序生成的汇编代码可以发现,A的默认构造函数被调用了:
6.对数组的处理
C++提供了new[]与delete[]来专门处理数组类型:
1
|
A
*
ptr
=
new
A
[
10
]
;
//分配10个A对象
|
使用new[]分配的内存必须使用delete[]进行释放:
1
|
delete
[
]
ptr
;
|
new对数组的支持体现在它会分别调用构造函数函数初始化每一个数组元素,释放对象时为每个对象调用析构函数。注意delete[]要与new[]配套使用,不然会找出数组对象部分释放的现象,造成内存泄漏。
至于malloc,它并知道你在这块内存上要放的数组还是啥别的东西,反正它就给你一块原始的内存,在给你个内存的地址就完事。所以如果要动态分配一个数组的内存,还需要我们手动自定数组的大小:
1
|
int
*
ptr
=
(
int
*
)
malloc
(
sizeof
(
int
)
*
10
)
;
//分配一个10个int元素的数组
|
7.new与malloc是否可以相互调用
operator new /operator delete的实现可以基于malloc,而malloc的实现不可以去调用new。下面是编写operator new /operator delete 的一种简单方式,其他版本也与之类似:
1
2
3
4
5
6
7
8
9
10
11
|
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
)
;
}
|
8.是否可以被重载
opeartor new /operator delete可以被重载。标准库是定义了operator new函数和operator delete函数的8个重载版本:
1
2
3
4
5
6
7
8
9
10
|
//这些版本可能抛出异常
void
*
operator
new
(
size_t
)
;
void
*
operator
new
[
]
(
size_t
)
;
void
*
operator
delete
(
void
*
)
noexcept
;
void
*
operator
delete
[
]
(
void
*
0)
noexcept
;
//这些版本承诺不抛出异常
void
*
operator
new
(
size
_t
,
nothrow_t
&
)
noexcept
;
void
*
operator
new
[
]
(
size_t
,
nothrow_t
&
)
;
void
*
operator
delete
(
void
*
,
nothrow_t
&
)
noexcept
;
void
*
operator
delete
[
]
(
void
*
0
,
nothrow_t
&
)
noexcept
;
|
我们可以自定义上面函数版本中的任意一个,前提是自定义版本必须位于全局作用域或者类作用域中。太细节的东西不在这里讲述,总之,我们知道我们有足够的自由去重载operator new /operator delete ,以决定我们的new与delete如何为对象分配内存,如何回收对象。
而malloc/free并不允许重载。
9. 能够直观地重新分配内存
使用malloc分配的内存后,如果在使用过程中发现内存不足,可以使用realloc函数进行内存重新分配实现内存的扩充。realloc先判断当前的指针所指内存是否有足够的连续空间,如果有,原地扩大可分配的内存地址,并且返回原来的地址指针;如果空间不够,先按照新指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来的内存区域。
new没有这样直观的配套设施来扩充内存。
10. 客户处理内存分配不足
在operator new抛出异常以反映一个未获得满足的需求之前,它会先调用一个用户指定的错误处理函数,这就是new-handler。new_handler是一个指针类型:
1
2
3
4
|
namespace
std
{
typedef
void
(
*
new_handler
)
(
)
;
}
|
指向了一个没有参数没有返回值的函数,即为错误处理函数。为了指定错误处理函数,客户需要调用set_new_handler,这是一个声明于的一个标准库函数:
1
2
3
4
|
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。
总结
将上面所述的10点差别整理成表格:
特征 | new/delete | malloc/free |
---|---|---|
分配内存的位置 | 自由存储区 | 堆 |
内存分配成功的返回值 | 完整类型指针 | void* |
内存分配失败的返回值 | 默认抛出异常 | 返回NULL |
分配内存的大小 | 由编译器根据类型计算得出 | 必须显式指定字节数 |
处理数组 | 有处理数组的new版本new[] | 需要用户计算数组的大小后进行内存分配 |
已分配内存的扩充 | 无法直观地处理 | 使用realloc简单完成 |
是否相互调用 | 可以,看具体的operator new/delete实现 | 不可调用new |
分配内存时内存不足 | 客户能够指定处理函数或重新制定分配器 | 无法通过用户代码进行处理 |
函数重载 | 允许 | 不允许 |
构造函数与析构函数 | 调用 | 不调用 |