C++ 类的成员函数定义在.h中为什么会产生函数重定义错误?
2015-12-27 13:41
441 查看
前言
此篇博客来源于我在百度知道的一个回答,当时回答的不太清楚,而且追答的时候又有字数的限制,不得已决定写个博客来。问题如下
F.h: void F(){} 1.cpp: #include"F.h" void main(){F()} 2.cpp: #include"F.h" void ff(){F()}
这样会报error LNK2005的错误。但为什么连接的时候会检查重定义?
问题描述
此程序有三个文件,F.h, 1.cpp, 2.cpp,两个cpp分别包含F.h,然后都调用F()函数,编译的时候报告error link 2005的错误。我的回答
首先在F.h中,你已经对F()函数进行了定义,这样你在1.cpp和2.cpp进行包含的时候,两个cpp都会有这个函数的定义。在编译期,编译期会根据F.h和1.cpp来编译生成1.o, 然后根据F.h和2.cpp编译生成2.o(请注意,1.o和2.o都有一个对于F函数的定义代码,注意注意是定义,不是声明),最后在链接器链接的时候,它发现在main函数中需要调用F(),于是开始寻找F的定义代码,很显然在1.o可以找到,在2.o也可以找到,那么链接器就晕菜了。同一个F函数,竟然有两个目标文件有定义,它不知道该选择哪一个了,于是就报重定义的链接错误。其实我感觉你是认为在1.cpp已经有F()函数的定义了,就直接拿来用就行了,不应该去2.cpp生成的o文件查看,不过编译器不是这样的。 你从编译器的角度来看看这个问题, 你在F.h定义了F()函数,这个函数被其他文件包含,这个函数又不是static,那么这个函数就具有全局的作用域,也就是说1.cpp和2.cpp中的F()的实现都具有全局的概念,或者你可以这样想,你完全可以在2.cpp有一个ff的函数的实现,然后在1.cpp的main中去调用ff,不过如果你直接在main中调用,在编译期就会报告这个ff找不到,因为编译器在编译1.cpp的时候,只会查看F.h,而不会查看2.cpp, 它发现找不到ff,虽然你的这个ff在2.cpp中确实有定义。 针对这种情况的一个做法是你在F.h中声明这个ff函数或者在1.cpp中通过extern前向声明ff函数,这两种方法都是通过明确告知编译器ff这个函数的定义是存在的(就是说你不用管ff的定义在哪儿,反正它就是存在,就是存在。),1.cpp既然认为ff存在,那么就可以编译1.cpp到1.o。 在链接器的时候,编译器需要将多个o文件链接成1个完整的exe文件,它寻找所有的.o文件,看看哪个里面有ff函数的定义(很幸运,在2.cpp生成的.o文件找到了ff的定义),这样它就可以修改1.o中的ff的调用地址为2.o中ff函数定义的地址(这个地方其实要做很多工作,因为需要将多个.o文件整合成一个exe,因此很多都需要加入地址偏移量),这样就可以生成exe了,如果在2.o中也没有ff函数,那么链接器就比较郁闷了,它会说ff未定义错误,虽然ff声明是有的,但是链接器在所有的.o文件中都无法找到ff,因此它就报错了。
如果还想知道一些更多的东西,建议看看动态链接库中的lib和dll,当编译引用库的时候,只需要lib就可以编译生成exe,但是在执行的时候,需要dll才可以正常执行。 这是因为这个里面的lib相当于告诉了编译器在2.o中ff的实现的地址,这样编译器在链接的时候,根据lib的描述,是可以正常生成exe的,然后在执行期,exe发现自己的这个地址是指向dll的,于是它开始寻找这个dll,但是找不到,就哭逼了,例如假设你给一个假的dll,它找到了这个dll,载入了这个dll,但是在执行具体函数的时候,在dll根据lib描述的地址去寻找,发现找不到,于是程序会直接崩溃,因为函数的地址给的是错误的。 也就是说lib给出了函数的地址,让编译器用于连接,dll给出了具体的函数实现代码的指令集,用于真正执行。
我的一些其他想法
本来想把上面的回答再完善一些,加入一些例子,demo以将重定义,未定义,找不到声明等等编译并连接错误演示一遍,并挖掘一下其后的原因,但是我明显发现最近关注c,c++的少多了,客户端开发多了起来,我也不清楚如果仔细的将这个关于c++编译链接的博客做的再好一些是否那么大的意义,因此这次我决定看看大家的反馈来决定后面继不继续详细阐述它。毕竟,深挖需要很大的工作量。角度
如果深挖的话,主要有下面的几个角度:1. c++各种常见的编译链接错误
2. c++静态库lib的一些看法
3. c++动态库lib和dll的一些看法
4. 大家提出自己遇到的c++编译或者链接的错误分享出来,我尽力在这里解释其错误背后的原因
目标
当深挖后,大家读完这样的博客,希望能给大家带来一些帮助,最好能够达成下面的目标:1. 对于c++编译链接错误的发生,不仅做到知其然,还要知其所以然,能够快速定位错误并改正它
2. 尽量能够覆盖当前c++编译的时候所遇到的常见错误
3. 关于编译环境引起的错误,自己研究的也很浅陋,可能就没法覆盖了。大家尽量在评论中分享这样的错误,这样其他网友也可以奉献自己的力量了。
期待大家的反馈!
本人lpstudy,转载请注明出处: http://blog.csdn.net/lpstudy/article/details/50379180相关文章推荐
- c++中的转义字符
- C++中string、int常见类型转换(含代码演示)
- C++编译器
- 如何用VC++6.0编写一个游戏
- C/C++中整数与浮点数在内存中的表示方式
- C/C++中整数与浮点数在内存中的表示方式
- C++中int、string等常见类型转换
- C++ 构造函数初始化列表
- C++笔记——虚析构函数的使用场景
- RedHat6.5安裝Gcc與Gcc-c++需要的包
- C++ Stack Implementation Discussion
- 如何用MinGW 命令行编译链接c/c++
- C++对象 内存分布
- 什么是POD?
- C++之路进阶——边表
- 【C++】深度探索C++对象模型之执行期语意学
- C语言学习笔记----------伊能C语言学习笔记-------静态局部变量
- C/C++学习(八)两个有序链表归并为一个有序链表
- c++标准14取消decltype推算函数返回类型
- C语言学习笔记----伊能C语言学习笔记-----全局变量