编译器处理相关 -----记 error LNK2005: XX already defined in DBHelper.obj in connectMysql Project...

本文主要是介绍编译器处理相关 -----记 error LNK2005: XX already defined in DBHelper.obj in connectMysql Project...,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

编译器处理相关
一.预处理器-编译器-汇编器-链接器
预处理器会处理相关的预处理指令,一般是以"#"开头的指令。如:#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 - Cagegory recompiled 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文件也
是需要时间的,如果在外部加上包含卫哨,对于很大的工程可以节省更多的编译时间。

5.待续(还有很多相关的东东,比如不同dll工程之间符号导出问题等等,有空再写)

__________________Trackback: http://tb.donews.net/TrackBack.aspx?PostId=211668




转载于:https://www.cnblogs.com/ToDoToTry/archive/2009/06/26/1511920.html

这篇关于编译器处理相关 -----记 error LNK2005: XX already defined in DBHelper.obj in connectMysql Project...的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/186867

相关文章

SQL Server数据库死锁处理超详细攻略

《SQLServer数据库死锁处理超详细攻略》SQLServer作为主流数据库管理系统,在高并发场景下可能面临死锁问题,影响系统性能和稳定性,这篇文章主要给大家介绍了关于SQLServer数据库死... 目录一、引言二、查询 Sqlserver 中造成死锁的 SPID三、用内置函数查询执行信息1. sp_w

Java对异常的认识与异常的处理小结

《Java对异常的认识与异常的处理小结》Java程序在运行时可能出现的错误或非正常情况称为异常,下面给大家介绍Java对异常的认识与异常的处理,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参... 目录一、认识异常与异常类型。二、异常的处理三、总结 一、认识异常与异常类型。(1)简单定义-什么是

CSS3中的字体及相关属性详解

《CSS3中的字体及相关属性详解》:本文主要介绍了CSS3中的字体及相关属性,详细内容请阅读本文,希望能对你有所帮助... 字体网页字体的三个来源:用户机器上安装的字体,放心使用。保存在第三方网站上的字体,例如Typekit和Google,可以link标签链接到你的页面上。保存在你自己Web服务器上的字

Golang 日志处理和正则处理的操作方法

《Golang日志处理和正则处理的操作方法》:本文主要介绍Golang日志处理和正则处理的操作方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考... 目录1、logx日志处理1.1、logx简介1.2、日志初始化与配置1.3、常用方法1.4、配合defer

springboot加载不到nacos配置中心的配置问题处理

《springboot加载不到nacos配置中心的配置问题处理》:本文主要介绍springboot加载不到nacos配置中心的配置问题处理,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录springboot加载不到nacos配置中心的配置两种可能Spring Boot 版本Nacos

python web 开发之Flask中间件与请求处理钩子的最佳实践

《pythonweb开发之Flask中间件与请求处理钩子的最佳实践》Flask作为轻量级Web框架,提供了灵活的请求处理机制,中间件和请求钩子允许开发者在请求处理的不同阶段插入自定义逻辑,实现诸如... 目录Flask中间件与请求处理钩子完全指南1. 引言2. 请求处理生命周期概述3. 请求钩子详解3.1

Python处理大量Excel文件的十个技巧分享

《Python处理大量Excel文件的十个技巧分享》每天被大量Excel文件折磨的你看过来!这是一份Python程序员整理的实用技巧,不说废话,直接上干货,文章通过代码示例讲解的非常详细,需要的朋友可... 目录一、批量读取多个Excel文件二、选择性读取工作表和列三、自动调整格式和样式四、智能数据清洗五、

SpringBoot如何对密码等敏感信息进行脱敏处理

《SpringBoot如何对密码等敏感信息进行脱敏处理》这篇文章主要为大家详细介绍了SpringBoot对密码等敏感信息进行脱敏处理的几个常用方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录​1. 配置文件敏感信息脱敏​​2. 日志脱敏​​3. API响应脱敏​​4. 其他注意事项​​总结

Python使用python-docx实现自动化处理Word文档

《Python使用python-docx实现自动化处理Word文档》这篇文章主要为大家展示了Python如何通过代码实现段落样式复制,HTML表格转Word表格以及动态生成可定制化模板的功能,感兴趣的... 目录一、引言二、核心功能模块解析1. 段落样式与图片复制2. html表格转Word表格3. 模板生

idea中project的显示问题及解决

《idea中project的显示问题及解决》:本文主要介绍idea中project的显示问题及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录idea中project的显示问题清除配置重China编程新生成配置总结idea中project的显示问题新建空的pr