effective C++: 8.定制new和delete(五)

2014-11-24 03:21:07 · 作者: · 浏览: 6
ding函数.这里假设new-handling函数也许能够做某些动作释放某些内


存.只有当当前的new-handling函数指针为null时,operator new才会抛出异常.
C++规定,即使客户要求0bytes,operator new也得返回一个合法指针.这种诡异的行为其实是为了简化语言的其它部分.下面是个non-member operator


new伪码:
void* operator new(std::size_t size) throw(std::bad_alloc)
{
using namespace std;
if( size == 0 ){
size = 1;
}
while( true ){
...//try to allocate size bytes memory.
if( allocate_succeed ){
return (point_to_allocted_memory);
}
//allocate failed:find current new-handling function(as following)
new_handler global_handler = set_new_handler( 0 );
set_new_handler( global_handler );

if( global_handler ){
( *global_handler )();
} else {
throw std::bad_alloc();
}
}
}
现在我们注意一下这里的一个可能会出现的问题:很多人没有意识到operator new成员函数会被derived classes继承,那就会出现,有可能base class的


operator new被调用用以分配derived class对象:
struct Base{
static void* operator new(std::size_t size) throw( std::bad_alloc );
...
};
struct Derived:public Base{...};
Derived* p = new Derived;//call Base::operator new.
如果Base class专属的operator new并非被设计对付上述情况(实际上往往如此),处理此情势的最佳做法是将'内存申请量错误'的调用行为改采标准


operator new,像这样:
void* Base::operator new(std::size_t size) throw(std::bad_alloc)
{
if( size != sizeof(Base) ){
return ::operator new( size ); //call standard operator new version.
}
...
}
如果你打算控制class专属之'arrays内存分配行为',那么你需要实现operator new的array兄弟版:operator new[].这个通常被称为"array new".如果


你要写这个operator new[],记住,唯一需要做的事情就是分配一块未加工内存.因为你无法对array之内迄今为止尚未存在的元素对象做任何事情.实际上你


甚至无法计算这个array将含有多少个元素.首先你不知道每个对象多大,因此你不能在Base::operator new[]内假设每个元素对象大小是sizeof(Base),此外


传递给它的参数size的值有可能比'将被填以对象'的内存数量更多.
这就是写operator new时候你需要奉行的规矩.operator delete情况更简单,你需要记住的唯一一件事情就是C++保证'删除null指针永远安全',所以你


必须兑现这项保证.下面就是non-member operator delete的伪码:
void operator delete( void* raw_memory ) thrwo()
{
if( raw_memory == 0 ){
return;
}
...//now,free raw memory block.
}
而对于member版本的也很简单.
void Base::operator delete( void* raw_memory,std::size_t size ) throw()
{
if( raw_memory == 0 ){
return;
}
if( size != sizeof(Base) ){ //if size error, call standard operator delete
::operator delete( raw_memory );
return;
}
...//now,free your raw memory block.
return;
}
如果即将删除的对象派生自某个base class而后者欠缺virtaul析构函数,那么C++传给operator delete的size_t数值可能不正确.这是'让你的base


class拥有virtual析构函数'的一个够好的理由.


请记住:
operator new应该内含一个无穷循环,并在其中尝试分配内存,如果它无法满足内存需求,就该调用new-handler.
它也应该有能力处理0bytes申请.class专属版本则还应该处理'比正确大小更大的(错误)申请'.
operator delete应该在收到null指针时不做任何事情.class专属版本则还应该处理'比正确大小更大的(错误)申请'.




条款52:写了placement new也要写placement delete


我们都知道当你在写一个new表达式像这样:
Widget* new_widget = new Widget;
共有两个函数被调用:一个是用以分配内存的operator new,一个是Widget的default构造函数.
那么假设我们现在遇到的情况是:第一个函数调用成功,第二个函数却抛出异常.按照常理,在步骤一中所分配的
内存必须取消,否则就会造成内存泄露.而在这个时候客户已经没有能力归还内存了,因为手上没有指向这块内存的
指针,故此任务就落到了C++运行期 系统身上.
为了完成任务,运行期系统当然就会调用步骤一所用的operator new的相应operator delete版本.如果当前要处
理的是拥有正常签名的new和delete版本,这好办!因为正常的operator new签名式:
void* operator new(std::size_t) throw (std::bad_alloc);
对应的正常的operator delete签名式:
void operator delete(void* raw_memory)throw();//global 作用域中的正常签名式
void operator delete(void* raw_memory,std::size_t size)throw();//class作用域中典型的签名式
因此,当你只使用正常形式的new和delete,运行期系统毫无问题可以找出那个'知道如何取消new所作所为并恢
复旧观'的delete.然而当你开始声明非正常形式的operator new,即就是附加参数的operator new,问题就出来了.
为了说明这个问题,我们依然用Widget例子,假设你写了一个class专属