📄 8610.htm
字号:
<p>c++不支持专门针对于类的new-handler函数,而且也不需要。你可以自己来实现它,只要在每个类中提供自己版本的set_new_handler和operator new。类的set_new_handler可以为类指定new-handler(就象标准的set_new_handler指定全局new-handler一样)。类的operator new则保证为类的对象分配内存时用类的new-handler取代全局new-handler。</p>
<p>假设处理类x内存分配失败的情况。因为operator new对类型x的对象分配内存失败时,每次都必须调用出错处理函数,所以要在类里声明一个new_handler类型的静态成员。那么类x看起来会象这样:<br> class x {<br> public:<br> static new_handler set_new_handler(new_handler p);<br> static void * operator new(size_t size);</p>
<p> private:<br> static new_handler currenthandler;<br> };</p>
<p>类的静态成员必须在类外定义。因为想借用静态对象的缺省初始化值0,所以定义x::currenthandler时没有去初始化。<br>new_handler x::currenthandler; // 缺省设置currenthandler为0(即null)</p>
<p>类x中的set_new_handler函数会保存传给它的任何指针,并返回在调用它之前所保存的任何指针。这正是标准版本的set_new_handler所做的:<br>new_handler x::set_new_handler(new_handler p)<br>{<br> new_handler oldhandler = currenthandler;<br> currenthandler = p;<br> return oldhandler;<br>}</p>
<p>最后看看x的operator new所做的:<br>1. 调用标准set_new_handler函数,输入参数为x的出错处理函数。这使得x的new-handler函数成为全局new-handler函数。注意下面的代码中,用了"::"符号显式地引用std空间(标准set_new_handler函数就存在于std空间)。</p>
<p>2. 调用全局operator new分配内存。如果第一次分配失败,全局operator new会调用x的new-handler,因为它刚刚(见1.)被安装成为全局new-handler。如果全局operator new最终未能分配到内存,它抛出std::bad_alloc异常,x的operator new会捕捉到它。x的operator new然后恢复最初被取代的全局new-handler函数,最后以抛出异常返回。</p>
<p>3. 假设全局operator new为类型x的对象分配内存成功,, x的operator new会再次调用标准set_new_handler来恢复最初的全局出错处理函数。最后返回分配成功的内存的指针。</p>
<p>c++是这么做的:<br>void * x::operator new(size_t size)<br>{<br> new_handler globalhandler = // 安装x的new_handler<br> std::set_new_handler(currenthandler); </p>
<p> void *memory;</p>
<p> try { // 尝试分配内存<br> memory = ::operator new(size); <br> }<br> catch (std::bad_alloc&) { // 恢复旧的new_handler<br> std::set_new_handler(globalhandler); <br> throw; // 抛出异常<br> } </p>
<p> std::set_new_handler(globalhandler); // 恢复旧的new_handler<br> <br> return memory;<br>}</p>
<p>如果你对上面重复调用std::set_new_handler看不顺眼,可以参见条款m9来除去它们。</p>
<p>使用类x的内存分配处理功能时大致如下:<br>void nomorememory(); // x的对象分配内存失败时调用的<br> // new_handler函数的声明<br> // </p>
<p>x::set_new_handler(nomorememory);<br> // 把nomorememory设置为x的<br> // new-handling函数</p>
<p>x *px1 = new x; // 如内存分配失败,<br> // 调用nomorememory</p>
<p>string *ps = new string; // 如内存分配失败,<br> // 调用全局new-handling函数<br> // </p>
<p>x::set_new_handler(0); // 设x的new-handling函数为空<br> // </p>
<p>x *px2 = new x; // 如内存分配失败,立即抛出异常<br> // (类x没有new-handling函数)<br> //</p>
<p>你会注意到,处理以上类似情况,如果不考虑类的话,实现代码是一样的,这就很自然地想到在别的地方也能重用它们。正如条款41所说明的,继承和模板可以用来设计可重用代码。在这里,我们把两种方法结合起来使用,从而满足了你的要求。</p>
<p>你只要创建一个“混合风格”(mixin-style)的基类,这种基类允许子类继承它某一特定的功能——这里指的是建立一个类的new-handler的功能。之所以设计一个基类,是为了让所有的子类可以继承set_new_handler和operator new功能,而设计模板是为了使每个子类有不同的currenthandler数据成员。这听起来很复杂,不过你会看到代码其实很熟悉。区别只不过是它现在可以被任何类重用了。<br>template<class t> // 提供类set_new_handler支持的<br>class newhandlersupport { // “混合风格”的基类<br>public: </p>
<p> static new_handler set_new_handler(new_handler p);<br> static void * operator new(size_t size);</p>
<p>private:<br> static new_handler currenthandler;<br>};</p>
<p>template<class t><br>new_handler newhandlersupport<t>::set_new_handler(new_handler p)<br>{<br> new_handler oldhandler = currenthandler;<br> currenthandler = p;<br> return oldhandler;<br>}</p>
<p>template<class t><br>void * newhandlersupport<t>::operator new(size_t size)<br>{<br> new_handler globalhandler =<br> std::set_new_handler(currenthandler);</p>
<p> void *memory;</p>
<p> try {<br> memory = ::operator new(size);<br> }<br> catch (std::bad_alloc&) {<br> std::set_new_handler(globalhandler);<br> throw;<br> }</p>
<p> std::set_new_handler(globalhandler);</p>
<p> return memory;<br>}</p>
<p>// this sets each currenthandler to 0<br>template<class t><br>new_handler newhandlersupport<t>::currenthandler;</p>
<p>有了这个模板类,对类x加上set_new_handler功能就很简单了:只要让x从newhandlersupport<x>继承:<br>// note inheritance from mixin base class template. (see<br>// my article on counting objects for information on why<br>// private inheritance might be preferable here.)<br>class x: public newhandlersupport<x> {</p>
<p> ... // as before, but no declarations for<br>}; // set_new_handler or operator new</p>
<p><br>使用x的时候依然不用理会它幕后在做些什么;老代码依然工作。这很好!那些你常不去理会的东西往往是最可信赖的。</p>
<p>使用set_new_handler是处理内存不够情况下一种方便,简单的方法。这比把每个new都包装在try模块里当然好多了。而且,newhandlersupport这样的模板使得向任何类增加一个特定的new-handler变得更简单。“混合风格”的继承不可避免地将话题引入到多继承上去,在转到这个话题前,你一定要先阅读条款43。</p>
<p>1993年前,c++一直要求在内存分配失败时operator new要返回0,现在则是要求operator new抛出std::bad_alloc异常。很多c++程序是在编译器开始支持新规范前写的。c++标准委员会不想放弃那些已有的遵循返回0规范的代码,所以他们提供了另外形式的operator new(以及operator new[]——见条款8)以继续提供返回0功能。这些形式被称为“无抛出”,因为他们没用过一个throw,而是在使用new的入口点采用了nothrow对象:<br>class widget { ... };</p>
<p>widget *pw1 = new widget; // 分配失败抛出std::bad_alloc if</p>
<p>if (pw1 == 0) ... // 这个检查一定失败<br>widget *pw2 =<br> new (nothrow) widget; // 若分配失败返回0</p>
<p>if (pw2 == 0) ... // 这个检查可能会成功</p>
<p>不管是用“正规”(即抛出异常)形式的new还是“无抛出”形式的new,重要的是你必须为内存分配失败做好准备。最简单的方法是使用set_new_handler,因为它对两种形式都有用。<br><br>
</p>
</DIV></div></div>
</center></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -