邓际锋ID:soloist
83622次访问,排名1121好友1人,关注者2
soloist的文章
原创 39 篇
翻译 0 篇
转载 0 篇
评论 189 篇
soloist的公告
欢迎吹毛求疵,感谢您对任何错误的指正,包括技术的、语法的、用词的、标点的、典故的、引用资料的……
最近评论
qingbai:lua绝对是个好东西。但国内除了java就是.net,其他东西没法活。因为程序员得工作,得吃饭。国内有哪家公司用lua?唉没办法呀。国外是一片繁荣,“百家争鸣”,国内是“青一色”的java和.net!无奈!
zhangyilan:尽管没有在实际代码的编写中碰到这个问题,不过也先学习一下,免得出现问题了搞出清楚情况。
ddrmsdos:这篇文章写的太好了,分析的非常仔细,以前常常碰到这类问题,终于解了我多年的心头之患......
ollydbg23:楼主的这篇文章写的非常好啊!
我看了以后,还是挺有收获感的,多谢多谢!
我也是对汇编,c++的比较感兴趣,有空可以交流一下!
w2001:写得很好
文章分类
收藏
    相册
    好博链接
    C++罗浮宫
    cpper
    fixopen
    fmddlmyy
    neoragex2002
    whinah
    云风
    梦想风暴
    沉思者
    许式伟
    负暄琐话
    辣子鸡丁
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 在VC6.0中如何让new操作失败后抛出异常?收藏

    新一篇: 雅虎招聘考试中两道C++题目的疑问 | 旧一篇: 为什么会出现LNK2005"符号已定义"的链接错误?

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

        VC6.0在<new>头文件中声明了这两种operator new操作符:

    void *__cdecl operator new(size_t) _THROW1(std::bad_alloc);
    void *__cdecl operator new(size_t, const std::nothrow_t&) _THROW0();

    并分别定义在newop.cpp和newop2.cpp中。而_THROW0和_THROW1则是两个宏,在Include目录的xstddef文件中定义:

    #define _THROW0()   throw ()
    #define _THROW1(x)  throw (x)

    newop.cpp和newop2.cpp对应的目标模块被打包进标准C++库中。标准C++库有若干个版本: libcp.lib(单线程静态版)、libcpd.lib(单线程静态调试版)、libcpmt.lib(多线程静态版)、libcpmtd.lib(多线程静态调试版)、msvcprt.lib(多线程动态版的导入库),msvcprtd.lib(多线程动态调试版的导入库),这些库与相应版本的C标准库一起使用,比如libcp.lib与libc.lib搭配。另外,VC6.0在new.cpp还定义了一个operator new,原型如下 :

    void * operator new( unsigned int cb )

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

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

    /***
    *use_ansi.h - pragmas for ANSI Standard C++ libraries
    *
    *       Copyright (c) 1996-1997, Microsoft Corporation. All rights reserved.
    *
    *Purpose:
    *       This header is intended to force the use of the appropriate ANSI
    *       Standard C++ libraries whenever it is included.
    *
    *       [Public]
    *
    ****/


    #if     _MSC_VER > 1000
    #pragma once
    #endif

    #ifndef _USE_ANSI_CPP
    #define _USE_ANSI_CPP

    #ifdef _MT
    #ifdef _DLL
    #ifdef _DEBUG
    #pragma comment(lib,"msvcprtd")
    #else // _DEBUG
    #pragma comment(lib,"msvcprt")
    #endif // _DEBUG

    #else // _DLL
    #ifdef _DEBUG
    #pragma comment(lib,"libcpmtd")
    #else // _DEBUG
    #pragma comment(lib,"libcpmt")
    #endif // _DEBUG
    #endif // _DLL

    #else // _MT
    #ifdef _DEBUG
    #pragma comment(lib,"libcpd")
    #else // _DEBUG
    #pragma comment(lib,"libcp")
    #endif // _DEBUG
    #endif

    #endif // _USE_ANSI_CPP


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

    // test.cpp
    #include <new>
    #include <iostream>
         
    using namespace std;
         
    class BigClass
    {
    public:
       BigClass() {}
       ~BigClass(){}
       char BigArray[0x7FFFFFFF];
    };
         
    int main()
    {
       try
       {
          BigClass *p = new BigClass;
       }
       catch( bad_alloc &a)
       {
          cout << "new BigClass, threw a bad_alloc exception" << endl;
       }
         
       BigClass *q = new(nothrow) BigClass;
       if ( q == NULL )
          cout << "new(nothrow) BigClass, returned a NULL pointer" << endl;

       try
       {
          BigClass *r = new BigClass[1];
       }
       catch( bad_alloc &a)
       {
          cout << "new BigClass[1], threw a bad_alloc exception" << endl;
       }

       return 0;
    }

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

    new(nothrow) BigClass, returned a NULL pointer

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

    dumpbin /SYMBOLS libcp.lib

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

    #if !defined(_MSC_EXTENSIONS)

    ...
    ...

    void *__cdecl operator new(size_t size) _THROW1(_STD bad_alloc)
    {
       ...
       ...
    }

    #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有什么冲突的地方。

        既然标准C++库里的newop.obj是个空壳,那我们就只好自己动手丰衣足食了。把newop.cpp和dbgint.h(都在VC98\crt\src目录下)拷贝到test.cpp所在的目录,并将newop.cpp中的

    #include <dbgint.h>

    改成

    #include "dbgint.h"

    然后用

    cl /c /Za /D_CRTBLD newop.cpp

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

    #ifndef _CRTBLD
    /*
     * This is an internal C runtime header file. It is used when building
     * the C runtimes only. It is not to be used as a public header file.
     */
    #error ERROR: Use of C runtime library internal header file.
    #endif
      /* _CRTBLD */

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

    cl /c /GX test.cpp

    最后进行链接:

    link test.obj newop.obj

    这时再运行test.exe输出的结果就是

    new BigClass, threw a bad_alloc exception
    new(nothrow) BigClass, returned a NULL pointer
    new BigClass[1], threw a bad_alloc exception

        值得庆幸的是虽然VC6.0如此弱智,但VC7.1却表现良好,原因是VC7.1的newop.cpp和newaop.cpp(数组版)取消了那个愚蠢的"#if !defined(_MSC_EXTENSIONS)",于是标准C++库中的newop.obj和newaop.obj模块都实实在在地有了相应代码。另外,nothrow版的定义也分别转移到了newopnt.cpp和newaopnt.cpp中。

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

    发表于 @ 2005年10月11日 23:14:00|评论(loading...)|编辑

    新一篇: 雅虎招聘考试中两道C++题目的疑问 | 旧一篇: 为什么会出现LNK2005"符号已定义"的链接错误?

    评论

    #cc 发表于2005-11-24 09:17:00  IP: 221.226.240.*
    good
    发表评论  


    当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
    Csdn Blog version 3.1a
    Copyright © soloist