您的位置:首页 > 编程语言 > C语言/C++

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: