本质来说,operator new函数就是对malloc函数的封装,operator delete函数就是对free函数的封装。
申请空间所使用的函数,new和malloc的区别之一,就是:
new失败后会抛出异常,而malloc失败则会直接返回零。
原因如下:
new在底层调用operator new全局函数来申请空间,delect在底层通过operator delete 全局函数来释放空间。
operator new:
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
//try to allocate size bytes
void *p;
while((p = malloc(size)) == 0)
if(_callnewh(size) == 0)
{
//report no memory
//如果申请内存失败了,这里会抛出bad_alloc类型异常
static const std::bad_alloc nomem;
_RAISE(nomem);
}
return (p);
}
operator delect:
void operator delete(void *pUserData)
{
_CrtMemBlockHeader * pHead;
RTCCALLBACK(_RTC_Free_hook,(pUserData,0));
if(pUserData == NULL)
return;
_mlock(_HEAP_LOCK); /*block other threads*/
_TRY
/*get a pointer to memory block header*/
pHead = pHdr(pUserData);
/*verify block type*/
_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
_free_dbg(pUserData, pHead->nBlockUse);
_FINALLY
_munlock(_HEAP_LOCK);/*release other threads*/
_END_TRY_FINALLY
return;
/*
free的实现
*/
#define free(p) _free_dbg(p,NORMAL_BLOCK)
}
通过上述两个全局函数的实现知道,operator new实际也是通过malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足以应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间。
下面采用反汇编来看一下new到底做了哪些事:
即申请空间operator new + 构造函数
关于delete的坑点:
class A
{
public:
A()
{
cout << "A()" << endl;
}
private:
int _a;
};
int main()
{
A* a = new A[10];
delete a;
return 0;
}
运行结果:无报错
下面再给A类添加一个析构函数:
class A
{
public:
A()
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
int main()
{
A* a = new A[10];
delete a;
return 0;
}
运行结果:程序出错
原因:
如果自定义了析构函数,那么编译器不会优化,必须调用析构函数。
A* a = new A[10];
编译器除了会申请10*4的空间,还会额外申请4字节的空间,用来存储申请空间的数量。
如下图所示:
如果直接进行delete,那么指针的位置并不是这个空间的初始位置(因为释放空间不能部分部分的释放),所以会报错。
如果没有定义析构函数,使用编译器生成的析构函数。此时如果编译器发现自生成的析构函数并没有什么实际的工作量,则会优化,不会额外申请4字节的空间,直接delete时指针位置则不会报错。
最保险的delete方式:delete[]
class A
{
public:
A()
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
int main()
{
A* a = new A[10];
delete []a;
return 0;
}
虽然没有写调用几次析构函数,但是编译器可以通过指针偏移,获取申请空间的个数,由此可知调
用析构函数的次数,进行释放空间。