条款51:编写new和delete时需固守常规

本文详细讲解了在C++中如何正确地实现自定义new和delete运算符,包括处理内存分配失败、确保内存请求的安全性以及如何适配不同大小的内存请求。

条款51:编写new和delete时需固守常规
    Adhere to convention when writing new and delete.
   
    大家好,前一条款我们已经讨论了你在什么时候想要写个自定义的operator new和operator delete,但并没有解释
当你这么做时必须遵守什么规则.我先来总体说一下,然后再分析这些规则.
    要实现一致性operator new必得返回正确的值,内存不足时必得调用new-handling函数(见条款49),必须有对付零
内存需求的准备,还需避免不慎掩盖正常形式的new(虽然这比较偏近class的接口要求而非实现要求).
    先来说说关于其返回值,如果它有能力供应客户申请的内存,就返回一个指针指向那块内存.如果没有那个能力,就
遵循49描述的原则,并抛出一个bad_alloc异常.
    而实际上operator new不止一次尝试进行内存分配,并在每次失败后调用new-handing函数.这里假设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专属版本则还应该处理'比正确大小更大的(错
误)申请'.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值