1.语法
malloc和new都是在堆区开辟内存空间并以指针的形式返回该片内存空间的地址出来,因此都得用一个指针去接受这个地址。
1.1malloc&free
malloc返回的是空指针的地址,没有类型,因此得进行强制类型转化。
malloc(),括号内填的是申请的内存大小,并以字节为单位,若想申请一个n个int型的数组的话 ,如下:
int n;
cin>>n;
int*p=(int*)malloc(n*sizeof(int));
1.2new&delete
因此返回的指针类型是已经决定的,用同类型的指针去接受地址即可; new的语法:
- new+数据类型+(数据)
int *p=new int;
int *q=new int(5);//可以直接开辟一个存入某个数据的内存空间
- new+数据类型+[数据个数]
int *r=new int [5]//可以开辟一个可以容纳某个个数的该数据类型的数组
//这个要与 new int(5)做区分
1.3关于释放内存(申请的堆区内存一定要还)
而free和delete是释放内存空间的,对指向分配内存的指针使用
//delete
delete p;//针对只开辟了一个单元的内存时
delete[] p;//针对开辟了一个若干单元的数组的内存时
//free
free(p);
2.new与malloc的区别
2.1 申请的内存所在位置
new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。自由存储区是C++基于new操作符的一个抽象概念。两个地方并不等价。凡是通过new操作符进行内存申请,该内存即为自由存储区。而堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配,C语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。
那么自由存储区是否能够是堆(问题等价于new是否能在堆上动态分配内存),这取决于operator new 的实现细节。自由存储区不仅可以是堆,还可以是静态存储区(全局区),这都看operator new在哪里为对象分配内存。
2.2返回类型安全性
- new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。
类型安全很大程度上可以等价于内存安全,类型安全的代码不会试图方法自己没被授权的内存区域。关于C++的类型安全性可说的又有很多了。
2.3内存分配失败时的返回值
malloc分配内存失败时返回NULL。 在使用C语言时,我们习惯在malloc分配内存后判断分配是否成功:
int *a = (int *)malloc ( sizeof (int ));
if(NULL == a)
{
...
}
else
{
...
}
new内存分配失败时,会抛出bac_alloc异常,它不会返回NULL;
写习惯了c中的方式,到c++中可能会这样:
int * a = new int();
if(NULL == a)
{
...
}
else
{
...
}
但这显然是错误的,因为new根本不会返回NULL,有毛病时也已经抛出异常了。正确的做法应该是使用异常机制:
try
{
int *a = new int();
}
catch (bad_alloc)
{
...
}
2.4是否需要指定内存大小
class A{...}
A * ptr = new A;
A * ptr = (A *)malloc(sizeof(A)); //需要显式指定所需内存大小sizeof(A);
使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算,而malloc则需要显式地指出所需内存的尺寸。
2.5是否可以被重载
opeartor new /operator delete可以被重载。标准库是定义了operator new函数和operator delete函数的8个重载版本:
//这些版本可能抛出异常
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如何为对象分配内存,如何回收对象。
3.一些案例
- 在栈区使用new
void fnc()
{
int *p=new int [5]
}
栈区new的话,指针p是存储在栈区,而它指向的new出来的内存在堆区。
函数结束后,因为p在栈区,p的内存会被清除(也即是new出来的空间的地址值),但是new出来的空间不会。
如果想在函数外清除这片内存的话,前提是你必须在函数结束时让这个指针传递出来。