effective C++: 8.定制new和delete(二)

2014-11-24 03:21:07 · 作者: · 浏览: 8
alloc);
private:
static std::new_handler currentHandler;
};


static class members(静态类成员)必须在 class 定义外被定义(除非它们是 const 而且是 integral ――参见 Item 2),所以:


std::new_handler Widget::currentHandler = 0; // init to null in the class
// impl. file


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 将做下面这些事情:


以 Widget 的 error-handling function 为参数调用 standard set_new_handler。这样将 Widget 的new-handler 安装为 global new-handler。
调用 global operator new 进行真正的内存分配。如果分配失败,global operator new 调用 Widget 的 new-handler,因为那个函数刚才被安装为


global new-handler。如果 global operator new 最后还是无法分配内存,它会抛出一个 bad_alloc exception。在此情况下,Widget 的 operator new


必须恢复原来的 global new-handler,然后传播那个 exception。为了确保原来的 new-handler 总能被恢复,Widget 将 global new-handler 作为一种


资源对待,并遵循 Item 13 的建议,使用 resource-managing objects(资源管理对象)来预防 resource leaks(资源泄漏)。
如果 global operator new 能够为一个 Widget object 分配足够的内存,Widget 的 operator new 返回一个指向被分配内存的指针。object 的用于管理


global new-handler 的 destructor(析构函数)自动将 global new-handler 恢复到调用 Widget 的 operator new 之前的状态。
以下就是你如何在 C++ 中表达这所有的事情。我们以 resource-handling class 开始,组成部分中除了基本的 RAII 操作(在构造过程中获得资源并在析


构过程中释放)(参见 Item 13),没有更多的东西:


class NewHandlerHolder {
public:
explicit NewHandlerHolder(std::new_handler nh) // acquire current
:handler(nh) {} // new-handler


~NewHandlerHolder() // release it
{ std::set_new_handler(handler); }
private:
std::new_handler handler; // remember it


NewHandlerHolder(const NewHandlerHolder&); // prevent copying
NewHandlerHolder& // (see Item 14)
operator=(const NewHandlerHolder&);
};


这使得 Widget 的 operator new 的实现非常简单:


void * Widget::operator new(std::size_t size) throw(std::bad_alloc)
{
NewHandlerHolder // install Widget's
h(std::set_new_handler(currentHandler)); // new-handler


return ::operator new(size); // allocate memory
// or throw


} // restore global
// new-handler


Widget 的客户像这样使用它的 new-handling capabilities(处理 new 的能力):


void outOfMem(); // decl. of func. to call if mem. alloc.
// for Widget objects fails


Widget::set_new_handler(outOfMem); // set outOfMem as Widget's
// new-handling function


Widget *pw1 = new Widget; // if memory allocation
// fails, call outOfMem


std::string *ps = new std::string; // if memory allocation fails,
// call the global new-handling
// function (if there is one)


Widget::set_new_handler(0); // set the Widget-specific
// new-handling function to
// nothing (i.e., null)


Widget *pw2 = new Widget; // if mem. alloc. fails, throw an
// exception immediately. (There is
// no new- handling function for
// class Widget.)


无论 class 是什么,实现这个方案的代码都是一样的,所以在其它地方重用它就是一个合理的目标。使它成为可能的一个简单方法是创建一个 "mixin-


style" base class(“混合风格”基类),也就是说,一个设计为允许 derived classes(派生类)继承一个单一特定能力(在当前情况下,就是设定一


个 class-specific new-handler 的能力)的 base class(基类)。然后把这个 base class(基类)转化为一个 template(模板),以便于你得到针对


每一个 inheriting class(继承来的类)的 class data 的不同拷贝。


这个设计的 base class(基类)部分让 derived classes(派生类)继承它们全都需要的 set_new_handler 和 operator new functions,而这个设计


template(模板)部分确保每一个 inheriting class(继承来的类)得到一个不同的 currentHandler data member(数据成员)。这听起来可能有点复杂


,但是代码看上去可靠而且熟悉。实际上,仅有的真正不同是