在c++中,无论是new还是delete,它们都被当成是运算符的,new运算符对应的是C语言中的malloc函数【在new的重载运算符函数里面调用malloc】,而delete自然对应的是C语言中的free函数了。当我们写下一句【int *p = new(7);】时,在编译转化后,最简单的版本也是这种:
//编译过后
int *p = _new( sizeof(int) ); //第一步
*p = 7; //第二步
但更多的为了安全起见,第二步的初始化操作应该在第一步内存配置成功之后再进行:
// 更先进的版本
int *p;
if( p = _new(sizeof(int)) )
*p = 7;
delete的情况和new类似,但delete比new更需要安全,比如一个空的NULL指针,最好不要对它有任何动作,所以一般的编译器都会为【delete p;】套上一层保护膜:
if( p != 0 )
_delete(p);
在这里需要特别注意是,【delete p;】并不是把p的指针值清零,而是把p所指向的堆上的内存资源回收释放掉,这意味着该内存地址上面的对象的生命周期已经结束,但是地址本身依然存在,所以p依然拥有一个合法的值,还能够继续使用,只不过受到了限制,被打成【void *】类型指针而已,所以像下面的代码就有一定危险性了:
if ( p != 0 ) //在delete p之后
...
那么对我们的自定义类类型来说,当我们用一个new在堆上给一个类开辟出类对象时,情况差不多也是类似,不同的是要使用该类的constructor类配置该对象,比如,当我们写下如此代码:
Point *p = new Point;
时,它将会被转化为:
Point *p;
if( p = _new(sizeof(Point) )
p = Point::Point(p);
但如果带有exception handling【异常处理】,那么转化结果要更为复杂一点:
Point *p;
if( p = _new(sizeof(Point) )
{
try{
p = Point::Point(p);
}
catch(...){
_delete(p); //调用delete library function来释放p配置的内存
throw; //将原来的exception上传!
}
}
我们在堆上new出一块内存后,然后用Point的constructor配置该内存,如果发生exception,那么首先把刚才配置出来的内存回收释放掉,然后再给上一级throw过去我们捕获的exception。
同样的道理,对于delete,类类型与内建类型还是有一点稍微的区别,毕竟多了一个析构器的调用:
//delete p;的转换
if( p != 0 )
{
Point::~Point(p);
_delete(p);
}
同理,我们再来看一下exception handling版下的destructor:
//delete p;的exception handling版本
if ( p != 0 )
{
try{ Point::~Point(p); }
catch(...){
_delete(p);
throw;
}
}
现在,让我们来看一下new的具体实现【模拟而已,不考虑exception handling】:
//有两处需要注意
extern void* operator new(size_t size)
{
if( size ==0 )
size = 1; //第一处,最少的也得是默认的1byte
void *last_alloc;
while( !(last_alloc = malloc(size)) )
{
if( _new_handler) //这是第二处需要注意的地方,看后续说明
(*_new_handler)( );
else return 0;
}
return last_alloc;
}
就在这里讲解一下第二处需要注意的地方吧,首先看while里面的表达式,“!( last_alloc = malloc(size) )”如果为真,意味着堆上并没有给我们开辟出可用内存资源【malloc返回null值】,这个时候就需要一些“申请内存失败”的异常处理,比如重新回到堆上回收一些无用的内存资源等等;而那个“_new_handler”其实是一个函数指针【就是处理内存申请失败的异常处理函数】,这里的意思是,可以让我们用户定义一个自己的new失败处理函数,然后把该函数地址赋值给“_new_handler”。
那么,怎么设置我们自己的处理函数呢?可以通过“set_new_handler( void (*new_handler) () ) throw()”,它定义在new标准库中:
namespace std
{
typedef void (*new_handler)();//表明new_handler是一个typedef,是一个既没有参数也没有返回值的函数的函数指针
new_handler ser_new_handler(new_handler pfun) throw();
}
我们可以随便做一个简单的处理函数,比如叫“My_Handler”,然后通过set_new_handler设置一下就可以了:
int My_Handler()
{
cout<<"对不起,内存申请失败!"<<endl;
return 0;
}
...
...
set_new_handler(My_Handler);//在这里设置一下