Effective C++ 条款51 编写new和delete时需固守常规

1. 实现定制的operator new和operator delete需要满足一定的要求.

    以operator new而言:实现一致性operator new必须返回正确的值;内存不足时必得调用new-handling函数;必须有对付零内存需求的准备;需避免不慎掩盖正常形式的new;如果有能力供应客户申请的内存,就返回一个指针指向该内存,反之就遵循条款49的规则并抛出bad_alloc异常;应该内含一个无限循环,知道成功分配内存或new-handling完成其功能......

    以上的要求中"必须有对付零内存需求的准备"指的是即使客户要求0 bytes,operator也得返回一个合法指针.

2. 下面是一个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){
        尝试分配size bytes;
        if(分配成功)
            return(一个指针,指向分配而来的内存);
        new_handler globalHandler=set_new_handler(0);
        set_new_handler(globalHandler);
        if(globalHandler)
            (*globalHandler)();
        else
            throw std::bad_alloc();
    }
}
View Code

    需要注意的是,operator new成员函数会被derived classes继承,这会导致某些有趣的复杂度.写出定制型内存管理器的一个常见理由是为针对某特定class的对象分配行为提供最优化,而不是为了该class的任何derived class,也就是说,这对某个类设计的operator,其行为可能很典型的只为大小刚好为该类型对象代销大小而设计.然而一旦被继承下去,有可能base class的operator new被调用用以分配derived class对象:

class Base{
public:
    static void* operator new(std::size_t size) throw(std::bad_alloc);
    ...
};
class Derived:public Base{
   ...
};
Derived*p =new Derived; //这里调用的是Base::operaotr new!
View Code

    Base class专属之operator new往往并不是被用来为Derived class对象分配内存,解决方法就是在"内存申请量错误"的调用行为改采标准的operator new:

void * Base::operator new(std::size_t size) throw(std::bad_alloc){
    if(size!=sizeof(Base))
        return ::operator new(size);
    ...
}
View Code

    如果要实现operator new[]的定制版,那么operator new[]唯一要做的就是分配一块内存,而无法对array内尚未存在的元素做任何事情,设置无法计算每个元素对象有多大,实际上,C++标准中operator new[]的缺省操作是经由operator new完成的(http://www.cplusplus.com/reference/new/operator%20new[]/?kw=operator%20new[]).

    operator delete的情况比较简单,唯一需要注意的是C++保证"删除null指针永远安全",下面是non-member operator delete的伪码:

void operator delete(void* rawMemory) throw(){
    if(rawMemory==0)
        return;
    归还rawMemory所指内存;
}
View Code

    operator delete的member版本也比较简单,只需要多加一个动作检查删除数量.万一class专属的operator new将大小有误的分配行为转交::operator new执行,大小有误的删除行为也必须转交::operator delete执行:

class Base{
public:
    static void* operator new(std::size_t size) throw(std::bad_alloc);
    static void operator delete(void* rawMemory,str::size_t size) throw();
    ...
};
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;
}
View Code

    需要注意的是,如果即将被删除的对象派生自某个base class而后者欠缺virtual析构函数,那么C++传给operator delete的size_t数值可能不正确.也就是说,operator delete的正常运作依赖于析构函数的正常运作(如果有析构函数的话).

原文地址:https://www.cnblogs.com/reasno/p/4803967.html