1.new/delete和malloc/free区别
(1)malloc/free是c语言的库函数,new/delete是C++的运算符。运算符可以重载,库函数不行。
(2)它们都能在堆上申请动态内存和释放内存。
(3)内部数据类型是编译器本来就认识的,不需要用户自己定义。如:int、char、double等等。非内部数据类型不是编译器本来就认识的,需要用户自己定义才能让编译器识别。如class myclass{…}
(4)对于非内部数据类型而言,Malloc/free无法满足动态对象的要求,因为对象在创建的同时要执行构造函数,对象在消亡的时候要执行析构函数。由于malloc/free是库函数,不在编译器的控制权限内,不能够自动实现构造函数和析构函数。
#include <iostream>
using namespace std;
class OBJECT
{
public:
OBJECT()
{
cout << "OBJECT() 构造函数" << endl;
}
~OBJECT()
{
cout << "~OBJECT() 析构函数" << endl;
}
};
void Use_malloc_free()
{
cout << "使用malloc/free" << endl;
OBJECT* p = (OBJECT*)malloc(sizeof(OBJECT));
free(p);
}
void Use_new_delete()
{
cout << "使用new/delete" << endl;
OBJECT* p = new OBJECT;
delete p;
}
int main()
{
Use_new_delete(); //用new/delete创建、销毁对象
cout << "----------------" << endl;
Use_malloc_free(); //用malloc/free创建、销毁对象
return 0;
}
运行结果:
使用new/delete
OBJECT() 构造函数
~OBJECT() 析构函数
----------------
使用malloc/free
可以看到malloc/free并没有自动执行对象的构造、析构函数。
简单来讲就是malloc只能分配内存,new可以分配内存,也可以构造对象;free只能释放内存,delete能够释放内存以及析构函数。
我们不能用malloc/free来完成动态对象的内存管理,应该用new/delete。由于“内部数据类型”没有析构和构造函数,这时使用两者是等价的。
注:由于C++程序经常调用C函数,所以malloc/free依然使用。
2.malloc和new的区别
(1)申请内存所在位置
new操作符是从自由存储区(free store)上为对象动态分配内存;malloc是在堆上动态分配内存。
自由存储区是C++基于new操作符的一个抽象概念;堆是操作系统上的术语,是操作系统所维护的一块特殊内存。
自由存储区可以是堆也可以是静态存储区,这都要看operator new在哪里为对象分配内存。
new还有一种变体:定位new,它能够让程序元指定要使用的位置(地址)。即new运算符提供了要使用的地址。
定位new运算符存在于头文件中。定位new只是返回传递给它的地址,并将其强制转化成void*,以便能够赋给任何指针类型。
#include <iostream>
using namespace std;
int main()
{
char buffer[100]; //缓冲区
cout << "buffer[100] address=" << (void*)buffer << endl;
cout << endl;
int* p1 = new int[10]; //常规new运算符
for (int i = 0; i < 10; i++)
{
p1[i] = 10-i;
}
cout << "p1 address=" << p1 << endl;
cout << "p1[0]=" << p1[0] << endl;
cout << endl;
int* p2;
p2 = new(buffer) int[10]; //定位new运算符
cout << "p2 address=" << p2 << endl;
for (int i = 0; i < 10; i++)
{
p2[i] = i;
}
cout << "p2[0]=" << p2[0] << endl; //在p2地址上的值,也是在buffer首地址上的值
cout << endl;
int* p3;
p3 = new(buffer) int; //定位new
cout << "p3 address=" << p3 << endl;
*p3 = 10; //将buffer首地址位置的数值改为10
cout << "*p3=" << *p3 << endl;
cout << endl;
int* p4;
p4 = new(buffer + 2 * sizeof(int))int;
cout << "p4 address=" << p4 << endl;//偏移10个int字节的新地址
cout << endl;
return 0;
}
运行结果
buffer[100] address=008FFC80
p1 address=00C50580
p1[0]=10
p2 address=008FFC80
p2[0]=0
p3 address=008FFC80
*p3=10
p4 address=008FFC88
p1的地址和buffer不一样,这是因为p1是新开辟的地址。
p2和buffer地址一样,这就是定位new的作用,可以看到p2的起始地址(即buffer的起始地址)的值为0
p3和buffer地址也一样,但是我们改变了数值大小,为10。
p4是相对于buffer偏移了2*4个字节,即8个字节。
关于定位new的隐患:
#include <iostream>
using namespace std;
class A
{
public:
A()
{
cout << "creat!" << endl;
}
~A()
{
cout << "destory!" << endl;
}
};
int main()
{
char buffer[100];
A *a = new(buffer) A;
return 0;
}
输出
creat!
并没有调用析构函数
加上如下代码
a.~A();
输出如下
creat!
destory!
此处不能使用delete a;
原因我还在想。
类在释放自己的空间前,会自动调用析构函数,但是在定位new这里,不能通过delete 语句释放对象,所以,对象就不能隐式的调用析构函数,需要在buffer释放前,显式的调用析构函数。
(2)返回类型安全性
new操作符内存分配成功后,返回的时对象类型的指针,类型严格与对象匹配,无须进行转换,所以new是符合安全类型的操作符。
malloc内存分配成功返回的是void*,需要强制转换成我们所需要的类型。
(3)内存分配失败的返回值
new内存分配失败后,会抛出bac_alloc异常,不会返回NULL;而malloc内存分配失败会返回NULL。
C语言
int *a = (int *)malloc ( sizeof (int ));
if(NULL == a)
{
...
}
else
{
...
}
C++语言
try
{
int *a = new int();
}
catch (bad_alloc)
{
...
}
(4)是否需要指定内存大小
new操作符申请内存不需要指定内存块的大小,编译器会根据类型自动计算;而malloc必须要显示的指出所需内存的大小。
class A{...}
A * ptr = new A;
A * ptr = (A *)malloc(sizeof(A)); //需要显式指定所需内存大小sizeof(A);
(5)是否调用析构函数/构造函数
使用new操作符来分配对象内存会经历三个阶段:
①调用operator new函数(数组为operator new[])分配一块足够大的、原始的、未命名的内存,一边存储特定类型的对象。
②编译器调用构造函数来构造对象,并传入初值。
③对象构造完毕后,返回该对象的指针。
使用delete来释放对象内存会经历两个步骤
①调用对象的析构函数
②编译器调用operator delete(或者operator delete[])函数释放空间。
然而malloc/free并不会这样,具体上面第1条刚讲过。
(6)对数组的处理
C++提供了new[]和delete[]来专门处理数组
A * ptr = new A[10];//分配10个A对象
delete [] ptr;
至于malloc,它并不知道你要在这块内存上放的是什么(数组还是单个整形变量),它只会给你一块原始内存以及它的地址;具体想要什么样的你自己去实现。例如:
int * ptr = (int *) malloc( sizeof(int)* 10 );//分配一个10个int元素的数组
(7)new和malloc是否可以相互调用
operator new / operator delete的实现可以基于malloc,而malloc的实现不能去调用new。
如下:一种简单实现
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个重载版本:
//这些版本可能抛出异常
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;
我们可以自定义上面版本的任何一个,前提是自定义版本必须位于全局作用域或者类内部。
然而malloc/free不支持重载。
(9)能够再重新分配内存
使用malloc分配内存之后发现内存不足,可以使用realloc函数进行内存的重新分配。
realloc函数先判断当前指针是否有足够的连续空间,如果有,原地扩大内存地址,并返回原来的地址指针;如果空间不够,就重新找到满足新指定大小的内存,分配空间,再将原有数据从头到尾拷贝到新的内存区域,再释放原来的内存区域。
new没有这样的操作。
(10)客户处理内存分配不足
malloc没有这样的功能。
在operator new无法满足某一个内存分配的需求的时候,它会抛出一个异常,在这之前,它会先调用一个客户制定的错误处理函数new_handler。为了指定这个来处理内存不足的函数,使用者需要调用标准库程序函数set_new_handler:
namespace std{
typedef void (*new_handler){ };//一个函数指针
new_handler set_new_handler(new_handler p) throw();
};
set_new_handler实际上就是一个形参和返回值类型都是new_handler的函数。
具体如下:
void outOfMem(){
std::cerr<<"Unable to statisfy request for memory\n";
std::abort();
}
int main()
{
set_new_handler(outOfMem);
int *pBigDataArray=new int[100000000L];
...
}
如果不能为operator new分配所需要的100000000个整数的空间,outOfMem就会被调用,于是程序终止(abort)。如果一直不能满足的话,outOfMem也就是之前所说的new_handler函数会不断被调用。
那么如何设计一个良好的new_handler:
①让更多的内存被使用。这样做的目的很简单,就是为了内存能够尽可能被operator new进行分配。实现此要求的做法是,当new_handler被调用的时候,将之前分配的内存释放给程序,以便能够继续使用。
②安装新的new_handler。如果当前的new_handler无法取得更多的内存,那么这个new_handler应该能够安装新的new_handler以便能够获得更多的内存空间。
③卸载new_handler,也就是将null指针传给set_new_handler。这样的话,一旦没有分配成功,就会抛出异常。
④抛出bad_alloc的异常。这样的异常不会被operator_new捕捉,因此会被传播到内存索求处。
⑤不返回。通常会调用abort或者exit。