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

第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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值