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

来源:互联网 发布:数据库查找 编辑:程序博客网 时间:2024/05/29 13:23

第8章   定制 new 和 delete

Customizing new and delete

    当计算机环境(例如Java和.NET)夸耀自己内置"垃圾回收能力"的当今,C++对内存管理的纯手工法也许看起来有点老气.但是许多苛刻的系统程序开发人员之所以选择C++,就是因为它允许他们手工管理内存.这样的开发人员研究并学习他们的软件使用内存的行为特征,然后修改分配和归还工作,以求获得其所建置的系统的最佳效率.
    这样做的前提是,了解C++内存管理例程的行为.这正是本章焦点.两个主角是分配例程和归还例程(allocation and deallocation routines,也就是 operator new 和 operator delete),配角是 new-handler,这是当 operator new 无法满足客户的内存需求时所调用的函数.
    多线程环境下的内存管理,遭受单线程系统不曾有过的挑战.由于heap是一个可被改动的全局性资源,因此多线程系统充斥着访问这一类资源的race conditions(竞速状态)出现机会.
    另外要记住的是,operator new 和 operator delete 只适合用来分配单一对象.Arrays所要的内存由 operator new[]分配出来,并由 operator delete[]归还(注意这两个函数名称中的[]).
    最后请注意,STL容器所使用的heap内存是由容器所拥有的分配器对象(allocator object)管理,不是被 new 和 delete 直接管理.

条款49: 了解 new-handler的行为

Understand the behavior of the new-handler

    当 operator new 无法满足某一内存分配需求时,它会抛出异常.当 operator new 抛出异常以反映一个未或满足的内存需求之前,它会先调用一个客户指定的错误处理函数,一个所谓的 new-handler.为了指定这个"用以处理内存不足"的函数,客户必须调用set_new_handler,那是声明于<new>的标准程序库函数:
namespace std {    typedef void (*new_handler)();    new_handler set_new_handler(new_handler p) throw();}
    如上所示,new_handler是个 typedef,定义出一个指针,该指针指向一个函数,这个函数参数为无,返回值类型为 void .set_new_handler则是"获得一个new_handler并返回一个new_handler"的函数.set_new_handler声明式尾端的一个"throw()"是一份异常明细,表示该函数不抛出任何异常.
    set_new_handler的参数是个指针,指向 operator new 无法分配足够内存时才被调用的函数.其返回值也是个指针,指向set_new_handler被调用前正在执行(但马上就要被替换)的那个 new-handler函数.
    可以这样使用set_new_handler:
// 以下是当operator new无法分配足够内存时,该被调用的函数void outOfMem() {    std::cerr << "Unable to satify request for memory\n";    std::abort();}int main() {    std::set_new_handler(outOfMem);    int* pBigDataArray = new int[100000000L];    ...}
    如果 operator new 无法为100000000个整数分配足够空间,outOfMem就会被调用,于是程序在发出一个信息后夭折(abort).
    当 operator new 无法满足内存申请时,它会不断调用 new-handler函数,直到找到足够内存.引起反复调用的代码显示于条款51.这里的高级描述已经足够获得一个结论,那就是一个设计良好的 new-handler函数必须做以下事情:
    1.让更多内存可被使用. 这便造成 operator new 内的下一次内存分配动作可能成功.实现此策略的一个做法是,程序一开始执行就分配一大块内存,而后当 new-handler第一次被调用,将它们释放给程序使用.
    2.安装另一个 new-handler. 如果目前这个 new-handler无法取得更多可用内存,或许它知道另外哪个 new-handler有此能力.
    3.卸除 new-handler. 也就是将null指针传给set_new_handler .一旦没有安装任何 new-handler,operator new 会在内存分配不成功时抛出异常.
    4.抛出 bad_alloc(或派生自bad_alloc)的异常. 这样的异常不会被 operator new 捕捉,因此会被传播到内存索求处.
    5.不返回. 通常调用abort或exit.
    假设打算处理Widget class 的内存分配失败情况.首先必须登录"当operator new无法为一个Widget对象分配足够内存时"调用的函数,所以需要声明一个类型为new_handler的 static 成员函数,用以指向 class Widget的 new-hanler.看起来像这样:
class Widget {public:    static std::new_handler set_new_handler(std::new_handler p) throw();    static void* operator new(std::size_t size) throw(std::bad_alloc);private:    static std::new_handler currentHandler;};
    static 成员必须在 class 定义式之外被定义(除非它们是 const 而且是整数型,详见条款2),所以需要这样写:
std::new_handler Widget::currentHandler = 0;
    Widget内的set_new_handler 函数会将它获得的指针存储起来,然后返回先前存储的指针,这也正是标准版set_new_handler的行为:
std::new_handler Widget::set_new_handler(std::new_handler p) throw() {    std::new_handler oldHandler = currentHandler;    currentHandler = p;    return oldHandler;}
    最后Widget的 operator new 做以下事情:
    1.调用标准set_new_handler,告知Widget的错误处理函数.
    2.调用global operator new,执行实际的内存分配.
    3.如果global operator new,能够分配足够一个Widget对象所用的内存,Widget的 operator new 会返回一个指针,指向分配所得.
    注意:
    set_new_handler允许客户指定一个函数,在内存分配无法获得满足时被调用.

0 0