使用STL,远离异常,VS2005

来源:互联网 发布:淘宝店铺认证后怎么办 编辑:程序博客网 时间:2024/04/18 16:30
编译chrome的代码时(xp sp3, vs2008,曾经在win7下编译过,未出现此问题,不知道为什么),发现了std::exception重复定义的问题,让我很抓狂。后来搜索到了这篇文章,发现只要在出现链接错误的.cc文件中加入着两行就可以了:
#define _HAS_EXCEPTIONS 0
#define _STATIC_CPPLIB




原文:

解决方法一:

如果你使用共享DLL版的运行时库,在include任意STL库之前,添加以下两行代码,并且在你的projectsetting中关闭异常。

#define _HAS_EXCEPTIONS 0
#define _STATIC_CPPLIB

如果你使用了运行时库中的静态版本的链接,这里没有又快又好地颁发。不过我这里倒是有个快但不好的解决途径。

解决方案二:

假设你写了以下的一段代码:

Simple code using STL

现在你到project settings中关闭exception,就像下面:

Project Setting 1

Project Settings 2

事情不会像你想的那么简单,你得到了一条warning信息:

1>c:\programfiles\microsoft visual studio 8\vc\include\vector(1141) : warningC4530: C++ exception handler used, but unwind semantics are notenabled. Specify /EHsc

( warning C4530: 使用了 C++异常处理程序,但未启用展开语义。请指定 /EHsc)

你可能会想,这只不过是一条warning而且不影响什么,但是如果你想关闭异常,以减小您的代码规模,这不是个好现象。好吧,双击这条信息,定位到一条STL库中的代码上。结果令人郁闷,但是这是个研究代码的好办法,哎,在几行之前,我们看到了一个有趣的define

#if _HAS_ITERATOR_DEBUGGING

好的,让我看看这个宏的定义吧,右键单击这个宏,转到它的定义,我们被带到了yvals.h文件。

又在几行之前,我们看到了以下几行代码:

#ifndef _HAS_EXCEPTIONS
#define _HAS_EXCEPTIONS 1
#endif

而且_HAS_NAMESPACE也已经被定义,就像以下代码:

#ifndef _HAS_NAMESPACE
#define _HAS_NAMESPACE 1
#endif

无论怎样,我们发现了一个方法来解决我们的问题。我们增加一行代码#define _HAS_EXCEPTIONS0并编译。我晕,这一点用都没有嘛。出现了很多链接错误,我是最讨厌这种错误的。

1>Main.obj : errorLNK2019: unresolved external symbol “__declspec(dllimport) public:void __thiscall std::exception::_Raise(void)const ”(__imp_?_Raise@exception@std@@QBEXXZ) referenced in function“protected: static void __cdecl std::vector>::_Xlen(void)”(?_Xlen@?$vector@HV?$allocator@H@std@@@std@@KAXXZ)
1>Main.obj : error LNK2019: unresolved externalsymbol“__declspec(dllimport) public:__thiscall std::exception::exception(char const *,int)”(__imp_??0exception@std@@QAE@PBDH@Z) referenced in function“public: __thiscall std::logic_error::logic_error(classstd::basic_string,class std::allocator > const&)”(??0logic_error@std@@QAE@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@1@@Z)

这里我们感兴趣的是__declspec(dllimport)部分。我们没有编译dll,所以这里不应该标记为import。_Xlen()表示它调用了_Raise()方法的length_error类是从logic_error所产生的异常方法,我们打开包含文件。

在exception类中有两个方法。_Raise()函数的前缀是__CLR_OR_THIS_CALL,exception的前缀是_CRTIMP2。

Visualassist表示_CRTIMP2在以下文件中被定义yvals.h,wctype.h,crtdefs.h和new.h。但是只有yvals.h和crtdefs.h被用到了。而且,_CRTIMP2在crtdefs.h是这样定义的:

#ifndef _CRTIMP2
#if defined(_DLL) &&!defined(_STATIC_CPPLIB)
#define _CRTIMP2 __declspec(dllimport)
#else
#define _CRTIMP2
#endif
#endif

显然_DLL在这里不起作用,因为我编译的是一个控制台程序,而_STATIC_CPPLIB没有被定义。

于是,我们有了解决方案。在includestl头文件前添加以下两行,就能解决问题。

#define _HAS_EXCEPTIONS 0
#define _STATIC_CPPLIB

但这并没有说明共享动态DLL的运行时库该怎么办。查看我们的代码没有_DLL被定义,但是查看编译选项发现它被显式定义在了CodeGeneration选项卡中。变成静态库后,产生新的链接错误。

1>LIBCMTD.lib(stdexcpt.obj) : error LNK2005:“public: virtual __thiscall std::exception::~exception(void)”(??1exception@std@@UAE@XZ) already defined in Main.obj
1>LIBCMTD.lib(stdexcpt.obj) : error LNK2005:“public: virtual char const * __thiscallstd::exception::what(void)const ” (?what@exception@std@@UBEPBDXZ)already defined in Main.obj

基于很多原因,你还是必须链接LIBCMTD.lib的,所以编译选项中必须要包括它。所以问题不只是文件被两次引用,而且还有文件已经编译了。这里有两个解决方法。

一、重新编译,关闭异常。

二、确保异常不会被重复产生。添加以下代码可以搞定,但这只是掩盖了问题,并没有解决。

#define _HAS_EXCEPTIONS 0
#define _EXCEPTION_
namespace std
{
class exception
{
public:
void _Raise() const {};
};

void _Throw( constexception& except );

class bad_alloc : publicexception
{
public:
bad_alloc( const char *_Message ) {};
};

}

--------------------------------------------------------------------------------------------------------------------------------
uafxcwd.lib(afxmem.obj) : error LNK2005 


uafxcwd.lib(afxmem.obj) : error LNK2005: "void * __cdecl operator new(unsigned int)" (??2@YAPAXI@Z) already defined in LIBCMTD.lib(new.obj)
uafxcwd.lib(afxmem.obj) : error LNK2005: "void __cdecl operator delete(void *)" (??3@YAXPAX@Z) already defined in LIBCMTD.lib(dbgdel.obj)
uafxcwd.lib(afxmem.obj) : error LNK2005: "void __cdecl operator delete[](void *)" (??_V@YAXPAX@Z) already defined in LIBCMTD.lib(delete2.obj)


原因:
CRT 库对 new、delete 和 DllMain 函数使用弱外部链接。MFC 库也包含 new、delete 和 DllMain 函数。这些函数要求先链接 MFC 库,然后再链接 CRT 库。
当 C 运行时 (CRT) 库和 Microsoft 基础类 (MFC) 库的链接顺序有误时,可能会出现以下 LNK2005 错误。


解决方法:
强制链接器按照正确的顺序链接库!
项目->属性->链接器->输入:附加依赖项添加 uafxcwd.lib


问题解决!
注意:uafxcwd.lib 库是 Debug 版本的,Release 版本的是 uafxcw.lib

0 0