Effective C++——条款51(第8章)

来源:互联网 发布:非正式会谈oo的淘宝店 编辑:程序博客网 时间:2024/06/07 06:48

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

Adhere to convention when writing new and delete.

    条款50已解释什么时候会想要写个自定义的 operator new 和 operator delete,但并没有解释当那么做时必须遵守什么规则.
    实现一致性 opertor new 必须得返回正确的值,内存不足时必须调用 new-handling 函数(详见条款49),必须有对付零内存需求的准备,还需避免不慎遮掩正常形式的 new.
    operator new 的返回值十分单纯.如果它有能力供应客户申请的内存,就返回一个指针指向那块内存.如果没有那个能力,就遵循条款49描述的规则,并抛出一个bad_alloc异常.

    然而其实也不是非常单纯,因为 operator new 实际上不只一次尝试分配内存,并在每次失败后调用 new-handling函数.这里假设 new-handling函数也许能够做某些动作将某些内存释放出来.只有当指向 new-handling函数的指针使null,operator new 才会抛出异常.
    C++规定,即使客户要求0 bytes,operator new 也得返回一个合法指针.这种看起诡异的行为其实是为了简化语言其他部分.下面是个non-member operator new 伪码(pseudocode):
void* operator new(std::size_t size) throw(std::bad_alloc) {    using namespace std;    if (size == 0) {        size = 1;       // 处理0-byte申请,将它视为1-byte申请    }    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();    }}
    这里的伎俩是把0 bytes申请量视为1 bytes申请量.这种做法简单,合法,可行.
    条款49谈到 operator new 内含一个无穷循环,而上述伪码明白表明这个循环:"while(true)"就是那个无穷循环.退出此循环的唯一办法就是:内存被成功分配或 new-handling函数做了一件描述于条款49的事情:让更多内存可用,安装另一个 new-handler,写出 new-handler,抛出bad_alloc异常,或承认失败而直接 return .现在,对于 new-handler为什么必须做出其中某些事情应该很清楚了,如果不那么做,operator new 内的 while 循环就永远不会结束.
    operator new 成员函数可能会被derived class 继承,注意上述 operator new 伪码中,函数尝试分配size bytes.那非常合理,因为size是函数接受的实参.然而就像条款50所言,写出定制型内存管理器的一个常见理由是针对某特定 class 的对象分配行为提供最优化,却不是为了该 class 的任何derived class .也就是说针对 class X而设计的 operator new,其行为很典型地只为大小刚好为sizeof(X)的对象而设计.然而一旦被继承下去,有可能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::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);    // 令标准的operator new处理    ...}
    operator delete 的情况更简单,需要记住的唯一事情就是C++保证"删除null指针永远安全",所以必须兑现这项保证.下面是non-member operator delete 的伪码(pseudocode):
void operator delete(void* rawMemory) throw() {    if (rawMemory == 0)        return ;    // 如果将被删除的是个null指针,那就什么都不做    现在,归还rawMemory所指的内存;}
    这个函数的member版本也很简单,只需要多加一个动作检查删除数量.
    注意:
    operator new 应该是内含一个无穷循环,并在其中尝试分配内存,如果它无法满足内存需求,就该调用 new-handler.它也应该有能力处理0 bytes申请. class 专属版本则还应该狐狸"比正确大小更大的(错误)申请".
    operator delete 应该在收到null指针时不做任何事. class 专属版本还应该处理"比正确大小更大的(错误)申请".

0 0
原创粉丝点击