zoukankan      html  css  js  c++  java
  • already defined in *.obj

    already defined in *.obj

    造成LNK2005错误主要有以下几种情况: 
    1
    .重复定义全局变量。可能存在两种情况:
     
    A
    、对于一些初学编程的程序员,有时候会以为需要使用全局变量的地方就可以使用定义申明一下。其实这是错误的,全局变量是针对整个工程的。正确的应该是在一个CPP文件中定义如下:int g_Test;那么在使用的CPP文件中就应该使用:extern int g_Test即可,如果还是使用int g_Test,那么就会产生LNK2005错误,一般错误错误信息类似:AAA.obj error LNK2005 int book c?book@@3HA already defined in BBB.obj。切记的就是不能给变量赋值否则还是会有LNK2005错误。 
                 
    这里需要的是“声明”,不是“定义”!根据C++标准的规定,一个变量是声明,必须同时满足两个条件,否则就是定义: 
    (1)
    声明必须使用extern关键字;(2)不能给变量赋初值 
    所以,下面的是声明
    extern int a; 
    下面的是定义 
    int a; int a = 0; extern int a =0; 
    B
    、对于那么编程不是那么严谨的程序员,总是在需要使用变量的文件中随意定义一个全局变量,并且对于变量名也不予考虑,这也往往容易造成变量名重复,而造成LNK2005错误。 

    2
    .头文件的包含重复。往往需要包含的头文件中含有变量、函数、类的定义,在其它使用的地方又不得不多次包含之,如果头文件中没有相关的宏等防止重复链接的措施,那么就会产生LNK2005错误。解决办法是在需要包含的头文件中做类似的处理:#ifndef MY_H_FILE      //如果没有定义这个宏 
    #define MY_H_FILE      //
    定义这个宏 
    …….      //
    头文件主体内容 
    ……. 
    #endif 
    上面是使用宏来做的,也可以使用预编译来做,在头文件中加入: 
    #pragma once 
    //
    头文件主体 
    3
    .使用第三方的库造成的。这种情况主要是C运行期函数库和MFC的库冲突造成的。具体的办法就是将那个提示出错的库放到另外一个库的前面。另外选择不同的C函数库,可能会引起这个错误。微软和C有两种C运行期函数库,一种是普通的函数库:LIBC.LIB,不支持多线程。另外一种是支持多线程的:msvcrt.lib。如果一个工程里,这两种函数库混合使用,可能会引起这个错误,一般情况下它需要MFC的库先于C运行期函数库被链接,因此建议使用支持多线程的msvcrt.lib。所以在使用第三方的库之前首先要知道它链接的是什么库,否则就可能造成LNK2005错误。如果不得不使用第三方的库,可以尝试按下面所说的方法修改,但不能保证一定能解决问题,前两种方法是微软提供的: 
    A
    、选择VC菜单Project->Settings->Link->Catagory选择Input,再在Ignore libraries 的Edit栏中填入你需要忽略的库,如:Nafxcwd.lib;Libcmtd.lib。然后在Object/library Modules的Edit栏中填入正确的库的顺序,这里需要你能确定什么是正确的顺序,呵呵,God bless you! 
    B
    、选择VC菜单Project->Settings->Link页,然后在Project Options的Edit栏中输入/verbose:lib,这样就可以在编译链接程序过程中在输出窗口看到链接的顺序了。 
    C
    、选择VC菜单Project->Settings->C/C++页,Catagory选择Code Generation后再在User Runtime libraray中选择MultiThread DLL等其他库,逐一尝试。 
    关于编译器的相关处理过程,参考: 
    http://www.donews.net/xzwenlan/archive/2004/12/23/211668.aspx 

    这就是我所遇到过的LNK2005错误的几种情况,肯定还有其他的情况也可能造成这种错误,所以我不希望你在看完这篇文章以后,再遇到LNK2005错误时候,不动脑筋的想对号入座的排除错误。编程的过程就是一个思考的过程,所以还是多多开动你的头脑,那样收获会更多!

    附录:

    编译器处理相关 
    一.预处理器-编译器-汇编器-链接器
    预处理器会处理相关的预处理指令,一般是以"#"开头的指令。如:#include "xx.h" #define等。
    编译器把对应的*.cpp翻译成*.s文件(汇编语言)。
    汇编器则处理*.s生成对应的*.o文件(obj目标文件)
    最后链接器把所有的*.o文件链接成一个可执行文件(?.exe)

    1.部件:
    首先要知道部件(可以暂且狭义地理解为一个类)一般分为头文件(我喜欢称为接口,如:*.h)及实现文件(如:*.cpp)。
    一般头文件会是放一些用来作声明的东东作为接口而存在的。
    而实现文件主要是实现的具体代码。

    2.编译单个文件:
    记住IDE在bulid文件时只编译实现文件(如*.cpp)来产生obj,在vc下你可以对某个?.cpp按下ctrl+f7单独编译它
    生成对应一个?.obj文件。在编译?.cpp时IDE会在?.cpp中按顺序处理用#include包括进来的头文件
    (如果该头文件中又#include有文件,同样会按顺序跟进处理各个头文件,如此递归。。)

    3.内部链接与外部链接:
    内、外链接是比较基础的东东,但是也是新手最容易错的地方,所以这里有必要祥细讨论一下。
    内部链接产生的符号只在本地?.obj中可见,而外部链接的符号是所有*.obj之间可见的。
    如:用inline的是内部链接,在文件头中直接声明的变量、不带inline的全局函数都是外部链接。
    在文件头中类的内部声明的函数(不带函数体)是外部链接,而带函数体一般会是内部链接(因为IDE会尽量把它作为内联函数)
    认识内部链接与外部链接有什么作用呢?下面用vc6举个例子:
    // 文件main.cpp内容:
    void main(){}
    // 
    文件t1.cpp内容:
    #include "a.h"
    void Test1(){ Foo(); }
    // 
    文件t2.cpp内容:
    #include "a.h"
    void Test2(){ Foo(); }
    // 
    文件a.h内容:
    void Foo( ){ }
    好,用vc生成一个空的console程序(File - new - projects - win32 console application),并关掉预编译选项开关
    (project - setting - Cagegoryrecompiled Headers - Not using precompiled headers)
    现在你打开t1.cpp按ctrl+f7编译生成t1.obj通过
    打开t2.cpp按ctrl+f7编译生成t2.obj通过
    而当你链接时会发现:
    Linking...
    t2.obj : error LNK2005: "void __cdecl Foo(void)" (
    ?Foo@@YAXXZ) already defined in t1.obj
    这是因为:
    1. 
    编译t1.cpp在处理到#include "a.h"中的Foo时看到的Foo函数原型定义是外部链接的,所以在t1.obj中记录Foo符号是外部的。
    2. 编译t2.cpp在处理到#include "a.h"中的Foo时看到的Foo函数原型定义是外部链接的,所以在t2.obj中记录Foo符号是外部的。
    3. 最后在链接 t1.obj 及 t2.obj 时, vc发现有两处地方(t1.obj和t2.obj中)定义了相同的外部符号(注意:是定义,外部符号可以
    多处声明但不可多处定义,因为外部符号是全局可见的,假设这时有t3.cpp声明用到了这个符号就不知道应该调用t1.obj
    中的还是t2.obj中的了),所以会报错。
    解决的办法有几种
    a.
    将a.h中的定义改写为声明,而用另一个文件a.cpp来存放函数体。(提示:把上述程序改来试试)
    (
    函数体放在其它任何一个cpp中如t1.cpp也可以,不过良好的习惯是用对应cpp文件来存放)。
    这时包括a.h的文件除了a.obj中有函数体代码外,
    其它包括a.h的cpp生成的obj文件都只有对应的符号而没有函数体,如t1.obj、t2.obj就只有符号,当最后链接时IDE会把
    a.obj的Foo()函数体链接进exe文件中,并把t1.obj、t2.obj中的Foo符号转换成对应在函数体exe文件中的地址。
    另外:当变量放在a.h中会变成全局变量的定义,如何让它变为声明呢?
    例如: 我们在a.h中加入:class CFoo{};CFoo* obj;
    这时按f7进行build时出现:
    Linking...
    t2.obj : error LNK2005: "class CFoo * obj" (
    ?obj@@3PAVCFoo@@A) already defined in t1.obj
    一个好办法就是在a.cpp中定义此变量( CFoo* obj,然后拷贝此定义到a.h文件中并在前面加上extern(extern CFoo* obj
    如此就可通过了。当然extern也可以在任何调用此变量的位置之前声明,不过强烈建议不要这么作,因为到处作用extern,会
    导致接口不统一。良好的习惯是接口一般就放到对应的头文件。

    b. 将a.h中的定义修改成内部链接,即加上inline关键字,这时每个t1.obj和t2.obj都存放有一份Foo函数体,但它们不是外部
    符号,所以不会被别的obj文件引用到,故不存在冲突。(提示:把上述程序改来试试)
    另外我作了个实验来验证”vc是把是否是外部符号的标志记录在obj文件中的“(有点绕口)。可以看看,如下:
    (1)
    文件内容:
    // 
    文件main.cpp内容:
    void main(){}
    // 
    文件t1.cpp内容:
    #include "a.h"
    void Test1(){ Foo(); }
    // 
    文件t2.cpp内容:
    #include "a.h"
    void Test2(){ Foo(); }
    // 
    文件a.h内容:
    inline void Foo( ){ }
    (2) 
    选t1.cpp按ctrl+f7单独编译,并把编译后的t1.obj修改成t1.obj_inline
    (3) 
    选t2.cpp按ctrl+f7单独编译,并把编译后的t2.obj修改成t2.obj_inline
    (4) 
    把除了t1.obj_inline及t2.obj_inline外的其它编译生成的文件删除。
    (5) 修改a.h内容为:void Foo( ){ },使之变为非内联函数作测试
    (6) rebuild all所有文件。这时提示:
    Linking...
    t2.obj : error LNK2005: "void __cdecl Foo(void)" (
    ?Foo@@YAXXZ) already defined in t1.obj
    Debug/cle.exe : fatal error LNK1169: one or more multiply defined symbols found
    (7) 
    好,看看工程目录下的debug目录中会看到新生成的obj文件。
    下面我们来手工链接看看,
    打开菜单中的project - setting - Link,拷贝Project options下的所有内容,如下:
    kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:yes /pdb:"Debug/cle.pdb" /debug /machine:I386 /out:"Debug/cle.exe" /pdbtype:sept 
    把它修改成:
    Link.exe kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:yes /pdb:"Debug/cle.pdb" /debug /machine:I386 /out:"Debug/cle.exe" /pdbtype:sept Debug/t1.obj Debug/t2.obj Debug/main.obj
    pause
    注意前面多了Link.exe,后面多了Debug/t1.obj Debug/t2.obj Debug/main.obj以及
    最后一个pause批处理命令,然后把它另存到工程目录(此目录下会看到debug目录)下起名为link.bat
    运行它,就会看到:
    t2.obj : error LNK2005: "void __cdecl Foo(void)" (
    ?Foo@@YAXXZ) already defined i
    n t1.obj
    Debug/cle.exe : fatal error LNK1169: one or more multiply defined symbols found
    很好,我们链接原来的obj文件得到的效果跟在vc中用rebuild all出来的效果一样。那么现在如果
    我们把备份出来的t1.obj_inline覆盖t1.obj而t2.obj_inline覆盖t2.obj再手动链接应该会是
    不会出错的,因为原t1.obj_inline及t2.obj_inline中存放的是内部链接符号。好运行Link.bat,果然
    不出所料,链接成功了,看看debug目录下多出了一个exe文件。这就说明了内或外符号在obj有标志标识!
    (
    提示:上述为什么不用vc的f7build链接呢,因为文件时间改变了,build会重新生成新的obj,
    所以我们用手动链接保证obj不变)[注bj信息可用dumpbin.exe查看]


    4.#include规则:
    有很多人不知道#include 文件该放在何处?

    1). 增强部件自身的完整性:
    为了保证部件完整,部件的cpp实现文件(如test.cpp)中第一个#include的应当是它自身对应的头文件(如test.h)。
    (除非你用预编译头文件, 预编译头必须放在第一个)。这样就保证了该部件头文件(test.h)所必须依赖的其它接口(如a.h等)要放到它对应的文件头中(test.h),而不是在cpp中(test.cpp)把所依赖的其它头文件(a.h等)移到其自身对应的头文件(test.h等)之前(因为这样强迫其它包括此部件的头文件(test.h)的文件(b.cpp)也必须再写一遍include(即b.cpp若要#include "test.h"也必须#include "a.h")”。另外我们一般会尽量减少文件头之间的依赖关系,看下面:

    2). 减少部件之间的依赖性:
    在1的基础上尽量把#include到的文件放在cpp中包括。
    这就要求我们一般不要在头文件中直接引用其它变量的实现,而是把此引用搬到实现文件中。
    例如
    // 
    文件foo.h:
    class CFoo{
    void Foo(){}
    };
    // 
    文件test.h:
    #include "foo.h"
    class CTest{
    CFoo* m_pFoo;
    public:
    CTest() : m_pFoo(NULL){}
    void Test(){ if(m_pFoo){ m_pFoo->Foo();}}
    ...........
    };
    // 
    文件test.cpp:
    #include "test.h"
    .....

    如上文件test.h中我们其实可以#include "foo.h"移到test.cpp文件中。因为CFoo* m_pFoo我们只想在部件CTest中用到,
    而将来想用到CTest部件而包括test.h的其它部件没有必要见到foo.h接口,所以我们用前向声明修改原文件如下:
    // 
    文件foo.h:
    class CFoo{
    public:
    void Foo(){}
    };
    // 
    文件test.h:
    class CFoo;
    class CTest{
    CFoo* m_pFoo;
    public:
    CTest();
    void Test();
    //........
    };
    // 
    文件test.cpp:
    #include "test.h" // 
    这里第一个放该部件自身对应的接口头文件
    #include "foo.h" // 该部件用到了foo.h
    CTest::CTest() : m_pFoo(0){ 
    m_pFoo = new CFoo; 
    }
    void CTest::Test(){ 
    if(m_pFoo){ 
    m_pFoo->Foo();
    }
    }
    //.....
    // 
    再加上main.cpp来测试:
    #include "test.h" // 
    这里我们就不用见到#include "foo.h"了
    CTest test;
    void main(){
    test.Test();
    }

    3). 双重包含卫哨:
    在文件头中包括其它头文件时(如:#include "xx.h")建议也加上包含卫哨:
    // test.h
    文件内容:
    #ifndef __XX1_H_
    #include "xx1.h"
    #endif
    #ifndef __XX2_H_
    #include "xx2.h"
    #endif
    ......

    虽然我们已经在xx.h文件中开头已经加过,但是因为编译器在打开#include文件也
    是需要时间的,如果在外部加上包含卫哨,对于很大的工程可以节省更多的编译时间。

  • 相关阅读:
    @RequestParam注解使用:Name for argument type [java.lang.String] not available, and parameter name information not found in class file either.
    cglib动态代理导致注解丢失问题及如何修改注解允许被继承
    springboot Autowired BeanNotOfRequiredTypeException
    git根据用户过滤提交记录
    不同包下,相同数据结构的两个类进行转换
    How to use Jackson to deserialise an array of objects
    jooq实践
    java如何寻找main函数对应的类
    Python--matplotlib
    Python 和 Scikit-Learn
  • 原文地址:https://www.cnblogs.com/fag888/p/5789136.html
Copyright © 2011-2022 走看看