编写new和delete时需固守常规
本章节介绍在自定义new和delete函数时,应该需要遵守哪些要求,为什么遵守这些要求。
首先,如下代码所述,当我们定义new的时候需要把size为0的内存申请考虑进去,为什么当内存申请为0时却分配1个字节的内存。大家想一下当你定义一个空的classA时,sizeof(A)占据大小是多小?很显然占据1个字节的空间,这是系统分配内存时在这种情况下遵守的规则,我们可以自定义自己的new,但是前提要迎合系统的规则。事实上,这样做是为了让语言其他部分方便操作。考虑byte为0的情况之后,我们开始分配内存空间,如果分配成功我们就返回什么的内存地址,如果分配失败我们就需要调用new-handler函数进行分配失败后的处理,这个函数的用处一般情况下就是释放一部分内存,重新尝试内存分配。我们设分配过程在一个无限循环里,所以真正写程序的时候肯定要有退出循环的语句,正如条款49提出的哪些方法。
void* operator new(std::size_t size) throw(std::bad_alloc)
{
using namespace std;
if(size==0){//处理0-byte申请
size=1;
}
while(true){
尝试分配size bytes;
if(分配成功)
return 指向分配得来的内存;
//分配失败,找到当前的new-handling函数
new_handler globalHandler=set_new_handler(0);
set_new_handler(globalHandler);
if(globalHandler) (*globalHandler)();
else throw std::bad_alloc();
}
}
正如条款50所说,我们定义内存分配器是为了特定的class对象,以此来优化,所以我们往往将内存分配器的功能封装在base class中,如下代码:
class Base{
public:
static void* operator new(std::size_t size) throw(std::bad_alloc)
……
};
class Derived:public Base
{……};//假设为重新定义operator new
Derived* p=new Derived;//这里调用了Base::operator new
如果是class专属的operator new,应该改为这样:
void* Base::operator new(std::size_t size) throw(std::bad_alloc)
{
if(size!=sizeof(Base))
return ::operator new(size);//使用标准的operator new
……
}
接着,事实上,由于base class一般都会比derived class小,所以,当申请大小不满足derived class时,我们调用标准的内存分配器。
然后,对于class专属版的arrays内存分配,需要实现operator new[]。编写operator new[]时,唯一要做的一件事就是分配一块未加工的内存(raw memory),而且内存足够大,因为你无法对array之内迄今尚未存在的元素对象做任何事。甚至我们无法知道这个array含有多少个元素对象。可能你也不知道每个对象多大,因为base class的operator new[]有可能经由继承被调用,将内存分配给derived class对象的array使用。所以不能再Base::operator new[]中假设每个元素对象大小是sizeof(Base),这样就是说你不能假设array元素个数是(bytes申请数/sizeof(Base))。此外,传递给operator new[]的size_t参数,其值有可能比将辈填对象的内存大一些,因为条款 16提过,动态分配的arrays可能包含额外空间用来存放元素个数。
其次,C++保证删除指针永远安全,所以在程序中,我们对于null指针的操作如下代码:
void operator delete(void* rawMemory) throw()
{
if(rawMemory==0) return;
归还rawMemory所指内存;
}
最后,,我们要始终记得new和delete的对立,new的处理方式和delete的处理方式要交相呼应。如下代码:
void Base::operator delete(void rawMemory, std::size_t size) throw()
{
if(rawMemory==0) return;
if(size!=sizeof(Base)){
::operator delete(rawMemory);
return ;
}
归还rawMemory所指内存;
return ;
}