动态网站制作指南 [  QQ表情  ]
[ 投票调查 ]
[ 企业邮箱 ]
[ 网站空间 ]
网络编程 | 站长之家 | 网页制作 | 图形图象 | 操作系统 | 冲浪宝典 | 软件教学 | 网络办公 | 邮件系统 | 网络安全 | 认证考试 | 系统进程
ASP源码 | .Net源码 | PHP源码 | JSP源码 | JAVA源码 | CGI源码 | VB源码 | C++源码 | Delphi源码 | PB源码 | VF源码 | 汇编 | 服务器
Firefox | IE | Maxthon | 迅雷 | 电驴 | BitComet | FlashGet | QQ | QQ空间 | Vista | 输入法 | Ghost | Word | Excel | wps | Powerpoint
asp | .net | php | jsp | Sql | c# | Ajax | xml | Dreamweaver | FrontPages | Javascript | css | photoshop | fireworks | Flash | Cad | Discuz!
当前位置 > 网站建设学院 > 网络编程 > C/C++教程
Tag:注入,存储过程,分页,安全,优化,xmlhttp,fso,jmail,application,session,防盗链,stream,无组件,组件,md5,乱码,缓存,加密,验证码,算法,cookies,ubb,正则表达式,水印,索引,日志,压缩,base64,url重写,上传,控件,Web.config,JDBC,函数,内存,PDF,迁移,结构,破解,编译,配置,进程,分词,IIS,Apache,Tomcat,phpmyadmin,Gzip,触发器,socket
网络编程:ASP教程,ASP.NET教程,PHP教程,JSP教程,C#教程,数据库,XML教程,Ajax,Java,Perl,Shell,VB教程,Delphi,C/C++教程,软件工程,J2EE/J2ME,移动开发
文章搜索服务
邮件订阅
输入你的邮件地址,
你将不会错过任何关于:
[ C/C++教程 ]的信息

本月文章推荐
.初学者编程入门:C++实用技巧讲解.
.实例解析C++/CLI中的接口与泛型.
.Winsocket编程之TCP/IP体系结构.
.C/C++程序员请注意指针的用法.
.用C语言解决鬼谷算题.
.C/C++中命令行参数的原理.
.C/C++是程序员必须掌握的语言吗?.
.获取WinNT/Win2k当前用户名和密码.
.C++程序的异常处理技巧.
.C程序开发初级讲座之循环结构.
.《俄罗斯方块》程序编写详解.
.C++对象布局及多态之虚成员函数调.
.c语言中static变量.
.UNIX 萤幕导向程式的发展利器.
.在CB中使用ACCESS数据库.
..
.C语言初学者入门讲座 第八讲 转移.
.编程修养-C语言篇.
.C++箴言:理解Terminology术语.
.用BCB实现禁止用户关闭Window系统.

C++箴言:理解 new-handler的行为

发表日期:2008-3-8 |



  当 operator new 不能满足一个内存分配请求时,它抛出一个 exception(异常)。很久以前,他返回一个 null pointer(空指针),而一些比较老的编译器还在这样做。你依然能达到以前的目的(在一定程度上),但是我要到本文的最后再讨论它。

  在 operator new 因回应一个无法满足的内存请求而抛出一个 exception 之前,它先调用一个可以由客户指定的被称为 new-handler 的 error-handling function(错误处理函数)。(这并不完全确切,operator new 真正做的事情比这个稍微复杂一些,具体细节将在下一篇文章中讨论。)为了指定 out-of-memory-handling function,客户调用 set_new_handler ——一个在 <new> 中声明的标准库函数:

namespace std {
 typedef void (*new_handler)();
 new_handler set_new_handler(new_handler p) throw();
}
  就像你能够看到的,new_handler 是一个指针的 typedef,这个指针指向不取得和返回任何东西的函数,而 set_new_handler 是一个取得和返回一个 new_handler 的函数。(set_new_handler 的声明的结尾处的 "throw()" 是一个 exception specification(异常规范)。它基本上是说这个函数不会抛出任何异常,尽管真相更有趣一些。关于细节,参见《C++箴言:争取异常安全的代码》。)

  set_new_handler 的形参是一个指向函数的指针,这个函数是 operator new 无法分配被请求的内存时应该调用的。set_new_handler 的返回值是一个指向函数的指针,这个函数是 set_new_handler 被调用前有效的目标。

  你可以像这样使用 set_new_handler:

// function to call if operator new can't allocate enough memory
void outOfMem()
{
 std::cerr << "Unable to satisfy request for memory\n";
 std::abort();
}
int main()
{
 std::set_new_handler(outOfMem);
 int *pBigDataArray = new int[100000000L];
 ...
}
  假如 operator new 不能为 100,000,000 个整数分配空间,outOfMem 将被调用,而程序将在发出一个错误信息后中止。(顺便说一句,考虑假如在写这个错误信息到 cerr... 的过程中内存必须被动态分配会发生什么。)

  当 operator new 不能满足一个内存请求时,它反复调用 new-handler function 直到它能找到足够的内存。但是从这种高层次的描述已足够推导出一个设计得好的 new-handler function 必须做到以下事情之一:

  ·Make more memory available(使得更多的内存可用)。这可能使得 operator new 中下一次内存分配的尝试成功。实现这一策略的一个方法是在程序启动时分配一大块内存,然后在 new-handler 第一次被调用时释放它供程序使用。

  ·Install a different new-handler(安装一个不同的 new-handler)。假如当前的 new-handler 不能做到使更多的内存可用,或许它知道有一个不同的 new-handler 可以做到。假如是这样,当前的 new-handler 能在它自己的位置上安装另一个 new-handler(通过调用 set_new_handler)。operator new 下一次调用 new-handler function 时,它会得到最近安装的那一个。(这个主线上的一个变化是让一个 new-handler 改变它自己的行为,这样,下一次它被调用时,可以做一些不同的事情。做到这一点的一个方法是让 new-handler 改变能影响 new-handler 行为的 static(静态),namespace-specific(名字空间专用)或 global(全局)的数据。)

  ·Deinstall the new-handler(卸载 new-handler),也就是,将空指针传给 set_new_handler。没有 new-handler 被安装,当内存分配没有成功时,operator new 抛出一个异常。

  ·Throw an exception(抛出一个异常),类型为 bad_alloc 或继续自 bad_alloc 的其它类型。这样的异常不会被 operator new 捕捉,所以它们将被传播到发出内存请求的地方。

  ·Not return(不再返回),典型情况下,调用 abort 或 exit。

  这些选择使你在实现 new-handler functions 时拥有极大的弹性。

  有时你可能希望根据被分配 object 的不同,用不同的方法处理内存分配的失败:

class X {
public:
 static void outOfMemory();
 ...
};
class Y {
public:
 static void outOfMemory();
 ...
};
X* p1 = new X; // if allocation is unsUCcessful,
// call X::outOfMemory

Y* p2 = new Y; // if allocation is unsuccessful,
// call Y::outOfMemory
  C++ 没有对 class-specific new-handlers 的支持,但是它也不需要。你可以自己实现这一行为。你只要让每一个 class 提供 set_new_handler 和 operator new 的它自己的版本即可。class 的 set_new_handler 答应客户为这个 class 指定 new-handler(正像standard set_new_handler 答应客户指定global new-handler)。class 的 operator new 确保当为 class objects 分配内存时,class-specific new-handler 代替 global new-handler 被使用。

  假设你要为 Widget class 处理内存分配失败。你就必须清楚当 operator new 不能为一个 Widget object 分配足够的内存时所调用的函数,所以你需要声明一个 new_handler 类型的 static member(静态成员)指向这个 class 的 new-handler function。Widget 看起来就像这样:


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 members(静态类成员)必须在 class 定义外被定义(除非它们是 const 而且是 integral),所以:

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 将做下面这些事情: Photoshop教程 数据结构 五笔输入法专题 QQ病毒专题 共享上网专题 Google工具和服务专题
  以 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 作为一种资源对待,并遵循《C++箴言:使用对象治理资源》中的建议,使用 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 操作(在构造过程中获得资源并在析构过程中释放)(《C++箴言:使用对象治理资源》),没有更多的东西:

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 《C++箴言:谨慎考虑资源治理类的拷贝行为》)
 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(数据成员)。这听起来可能有点复杂,但是代码看上去可靠而且熟悉。实际上,仅有的真正不同是它现在可以用在任何需要它的 class 之上:

template<typename T> // "mixin-style" base class for
class NewHandlerSupport{
 // class-specific set_new_handler
public: // support

 static std::new_handler set_new_handler(std::new_handler p) throw();
 static void * operator new(std::size_t size) throw(std::bad_alloc);

 ... // other versions of op. new
private:
 static std::new_handler currentHandler;
};

template<typename T>
std::new_handler
NewHandlerSupport<T>::set_new_handler(std::new_handler p) throw()
{
 std::new_handler oldHandler = currentHandler;
 currentHandler = p;
 return oldHandler;
}

template<typename T>
void* NewHandlerSupport<T>::operator new(std::size_t size)
throw(std::bad_alloc)
{
 NewHandlerHolder h(std::set_new_handler(currentHandler));
 return ::operator new(size);
}
// this initializes each currentHandler to null
template<typename T>
std::new_handler NewHandlerSupport<T>::currentHandler = 0;
  有了这个 class template(类模板),为 Widget 增加 set_new_handler 支持就很轻易了:Widget 只需要从 NewHandlerSupport<Widget> 继续即可。(可能看起来很奇异,但是下面我将解释更多的细节。)

class Widget: public NewHandlerSupport<Widget> {
 ... // as before, but without declarations for
}; // set_new_handler or operator new
  这些就是 Widget 为了提供一个 class-specific set_new_handler 所需要做的全部。

  但是也许你依然在为 Widget 从 NewHandlerSupport<Widget> 继续而烦恼。假如是这样,当你注重到 NewHandlerSupport template 从来没有用到它的 type parameter T 时,你可能会更加烦恼。它不需要那样做。我们需要的全部就是为每一个从 NewHandlerSupport 继续的 class 提供一份不同的 NewHandlerSupport ——非凡是它的 static data member(静态数据成员)currentHandler ——的拷贝。template parameter T 只是为了将一个 inheriting class 同另一个区分开来。template 机制自己自动地为每一个被实例化的 NewHandlerSupport 中的 T 生成一个 currentHandler 的拷贝。

  对于 Widget 从一个把 Widget 当作一个 type parameter(类型参数)的 templatized base class(模板化基类)继续,假如这个概念把你弄得有点糊涂,不必难受。它最开始对每一个人都有这种影响。然而,它发展成如此有用的一项技术,它有一个名字,虽然它正常看上去所反映的事实并不是他们第一次看到它的样子。它被称作 curiously recurring template pattern(奇异的递归模板模式) (CRTP)。真的。

  在这一点上,我发表了一篇文章建议一个更好的名字叫做 "Do It For Me",因为当 Widget 从 NewHandlerSupport<Widget> 继续时,它其实是在说:“我是 Widget,而我要从针对 Widget 的 NewHandlerSupport class 继续。”没有人使用我提议的名字(甚至是我自己),但是把 CRTP 考虑成说 "do it for me" 的一种方式也许会帮助你理解 templatized inheritance(模板化继续)在做些什么。

  像 NewHandlerSupport 这样的 templates 使得为任何有需要的 class 添加一个 class-specific new-handler 变得易如反掌。然而,mixin-style inheritance(混合风格继续)总是会导致 multiple inheritance(多继续)的话题,而在我们沿着这条路走下去之前,你需要阅读《C++箴言:谨慎使用多继续》。

  直到 1993 年,C++ 还要求 operator new 不能分配被请求的内存时要返回 null。operator new 现在则被指定抛出一个 bad_alloc exception,但是很多 C++ 程序是在编译器开始支持这个修订标准之前写成的。C++ 标准化委员会不想遗弃这些 test-for-null(检验是否为 null)的代码基础,所以他们提供了 operator new 的另一种可选形式,用以提供传统的 failure-yields-null(失败导致 null)的行为。这些形式被称为 "nothrow" 形式,这在一定程度上是因为它们在使用 new 的地方使用了 nothrow objects(定义在头文件 <new> 中):


class Widget { ... };
Widget *pw1 = new Widget; // throws bad_alloc if
// allocation fails

if (pw1 == 0) ... // this test must fail

Widget *pw2 =new (std::nothrow) Widget; // returns 0 if allocation for
// the Widget fails

if (pw2 == 0) ... // this test may succeed
  对于异常,nothrow new 提供了比最初看上去更少的强制保证。在表达式 "new (std::nothrow) Widget" 中,发生了两件事。首先,operator new 的 nothrow 版本被调用来为一个 Widget object 分配足够的内存。假如这个分配失败,众所周知,operator new 返回 null pointer。然而,假如它成功了,Widget constructor 被调用,而在此刻,所有打的赌都失效了。Widget constructor 能做任何它想做的事。它可能自己 new 出来一些内存,而假如它这样做了,它并没有被强迫使用 nothrow new。那么,虽然在 "new (std::nothrow) Widget" 中调用的 operator new 不会抛出,Widget constructor 却可以。假如它这样做了,exception 像往常一样被传播。结论?使用 nothrow new 只能保证 operator new 不会抛出,不能保证一个像 "new (std::nothrow) Widget" 这样的表达式绝不会导致一个 exception。在所有的可能性中,你最好绝不需要 nothrow new。

  无论你是使用 "normal"(也就是说,exception-throwing)new,还是它的稍微有些矮小的堂兄弟,理解 new-handler 的行为是很重要的,因为它可以用于两种形式。

  Things to Remember

  ·set_new_handler 答应你指定一个当内存分配请求不能被满足时可以被调用的函数。

  ·nothrow new 作用有限,因为它仅适用于内存分配,随后的 constructor 调用可能依然会抛出 exceptions。
上一篇:COM组件接口继承的实现 人气:455
下一篇:C语言初学者的困惑--如何由dos向windows转变 人气:348
浏览全部C/C++的内容 Dreamweaver插件下载 常用网页广告代码全集
  最新网站源码 最新软件下载
2008-9-7 站长中国企业(公司)网站系统 v4.2
2008-9-7 PBDigg v2.0 Build 20080821
2008-9-7 玩玩小游戏FLASH系统 v2.1
2008-9-7 522QQ在线电视直播程序 v1.1
2008-9-7 Pcook cms 文章管理系统 (老Y CM
2008-9-7 仿代码小说小偷系统 v1.0
2008-9-7 百度一搜集成搜索管理系统
2008-9-7 小贤统计器 v1.0
2008-9-7 UCenter Home-中秋搏饼插件 v1.2
2008-9-7 iBlacklist通话黑名单汉化破解补
2008-9-7 EndlessWalls无尽壁纸 v1.0.4破解
2008-9-7 Dynolicious车载测量仪v1.1破解版
2008-9-7 iVoodoo巫毒娃娃1.0.1破解版
2008-9-7 iWallpape精品墙纸1.2破解版
2008-9-7 iChillout自然音效工具1.1破解版
2008-9-7 Todo计划提醒1.2破解版
2008-9-7 allRadio电台集合1.01破解版
2008-9-7 My Money个人理财1.0破解版
  发表评论
姓 名: 验证码:
内 容:
站长工具:网站收录查询 | Google PR查询 | ALEXA排名查询 | CSS在线编辑器 | 广告代码 | Html转换js | js/vbs加密 | md5加密 | 进制转换
实用工具:汉字翻译拼音 | 符号对照表 | 个税计算 | 经典小工具 | 汉字简繁转换 | 普通单位换算 | 公制单位换算 | 生辰老黄历 | 国内电话区号 国家代码与域名缩写 | 文字加密解密 | 健康查询 | 万年历 | 汉字横竖排版 | 手机号码查询 | 计算器 | ip搜索
业务联系 | 广告刊登 | 频道合作 | 投稿荐稿 | 联系方式 | 加入收藏 | RSS订阅
Copyright © 2000-2008 www.knowsky.com All rights reserved | 网络实名:动态网站制作指南 | 沪ICP备05001343号
ホームページ制作 不動産検索システム 求人情報
防水工事·改修工事 フットサル大会 探偵