我要投搞

标签云

收藏小站

爱尚经典语录、名言、句子、散文、日志、唯美图片

当前位置:金算盘心水论坛 > 抛出异常 >

在VC60中如何让new操作失败后抛出异常?

归档日期:05-24       文本归类:抛出异常      文章编辑:爱尚语录

  标准C++规定new一个对象时如果分配内存失败就应抛出一个std::bad_alloc异常,如果不希望抛出异常而仅仅传回一个NULL指针,可以用new的无异常版本:new(nothrow)。

  而new.cpp对应的目标模块却是被打包进C标准库中的(是不是有点奇怪?)。

  一般来说,程序员不会显式指定链接C++标准库,可是当程序中确实使用了标准C++库时链接器却能聪明地把相应的C++标准库文件加进输入文件列表,这是为什么?其实任何一个C++标准头文件都会直接或间接地包含use_ansi.h文件,打开它一看便什么都清楚了(源码之前,了无秘密) :

  现在我们用实际代码来测试一下new会不会抛出异常,建一个test.cpp源文件:

  根据VC6.0编译器与链接器的做法(请参考《为什么会出现LNK2005符号已定义的链接错误?》),链接器会首先在C++标准库中解析符号,然后才是C标准库,所以如果开发者没有自定义operator new的话最后程序链接的应该是C++标准库中newop.obj和newop2.obj模块里的代码。可是程序运行的结果却是:

  显然程序始终未抛出bad_alloc异常。单步跟踪观察,发现第1个和第3个new实际上调用了new.cpp里的operator new,而第二个new(nothrow)则正确地调用了newop2.cpp定义的版本。很难理解是吧?但是当你用

  dump出libcp.lib所有的符号信息时,你会发现其中的newop.obj模块没有定义任何符号(其它版本也一样)。不可思议!newop.cpp的实现代码明明写在那儿,怎么会....?让我们再仔细看看newop.cpp,咦,operator new的定义被包裹在一个#if...#endif块中:

  哦,原来需要_MSC_EXTENSIONS宏未定义,实现代码才是有效的啊。那么这个宏是什么意思?其实Visual C++在语言层面上对ANSI C标准做了一些特殊的扩展,定义_MSC_EXTENSIONS意味着编译器支持这样的扩展,没有定义它编译器就会严格按照ANSI C标准来编译程序。实际上如果指定了编译选项/Ze编译器就会自动定义这个宏,指定/Za则不会,而且/Ze是缺省选项。作者猜想Visual Studio的开发人员在build标准C++库时很可能没有指定/Za,导致newop.cpp中的operator new定义被无情抛弃。是他们的疏漏吗?我看未必,大家可以试试用/Za选项去编译那些标准库文件,看看有多少编译不通过。VC标准库的实现用了很多微软扩展的语言特性,不指定/Za是情有可原的,我不明白的是newop.cpp的作者(好象是P.J. Plauger老人家)为什么会加上一个如此愚蠢的#if !defined(_MSC_EXTENSIONS),因为实在看不出这个operator new定义与_MSC_EXTENSIONS有什么冲突的地方。

  编译它。/D_CRTBLD定义了_CRTBLD宏,为什么这么做呢?因为dbgint.h属于内部头文件,VC不希望应用程序用到它,便在文件中埋伏了这么一段:

  可我们确确实实是想build标准库(的一部分),所以只好强行突破这个限制了。然后编译test.cpp:

  后记: 作者在2001年便碰到过这个问题,百思不得其解,于是在CSDN论坛上发问,也不见答复。从此便搁置一旁,直到最近因探究LNK2005链接错误而彻底弄清楚VC链接器解析符号的规则后,才意识到二者或有联系。于是重拾旧疑,顺藤而上,果然问题就迎刃而解。此题虽小,功夫却做足,最后总算水落石出,解除了4年的积惑。

本文链接:http://izytravel.com/paochuyichang/88.html