在C++编程过程中,出的一类较多的错误就是“error LNK2019: 无法解析的外部符号”,一般来说,对于语法错误的提示,往往可以在较短的时间里修正并取得成效,如果是通过测试发现的逻辑错误呢也可以通过跟踪调试找到问题所在,当然后者排错花费的精力相对要大些,不过终归使用正规的路子还是可以顺藤摸瓜解决问题的,最令初学者头疼的就是link error链接错误,只要编译器报连接错误,往往让大家丈二和尚摸不着头脑,下面就我个人的经验谈谈出现link错误及排解的方法:
原因一:用户自定义的类中的函数声明与函数定义分开写时,函数名写的不一致,往往初学者比较容易犯这样的毛病,比如,在类的头文件Point.h里声明了void setX(int aX);函数,在Point.cpp文件中写该函数的定义时确写成了void Point::setx(int aX){},由于C++语言是区分大小写的编程语言,所以在编译时认定为只有该函数setX的声明,没有其定义,当然这种情况往往容易发现错误,因为计算机会先报语法声明错误,告知setx函数没有在类Point中声明,然而,可怕的是在.h文件里声明了setX函数,却忘记了在.cpp文件里定义,这时,在程序中调用过几次setX函数就会出现几个link链接错误,初学者往往由于大量的链接错误而灰心丧气,其实如果知道原因了修改起来并不难。
避错方案:将声明好的类写好后,复制,然后粘贴到.cpp文件中完成其定义过程,需要提醒的是,注意去掉每个函数声明后面的分号。
原因二:未引入链接库.lib文件,这个多发生在使用第三方开发库的情况下,比如,我在博客中系列提到的SDL开发,开发时需要在编译的时候链接SDL提供的lib库,如果没有通过配置的方式(配置的方法请参考我前面SDL配置相关文章,这里不做赘述)添加相关.lib文件或者通过#pragma comment(lib, "SDL.lib")提供lib的链接,那么计算机会因为找不到所调用函数的代码段(这种情况下,只有头文件,代码段被编译好了放在.lib文件里)而报link错误,改正的方法就不言而喻了。
避错方案:先看自己的工程有没有用到第三方开发环境,如果用到了,请先把这些配好,再开始后续的开发工作。另外,不是只有第三方开发环境才需要配置相应的.lib文件,当我们在VS2005/2008(暂且以他们为例,其它同理)中,按alt+F7打开工程属性页后,点选配置属性-->链接器-->输入,可以看到默认添加好的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
在开发过程中如果用到之外的库(这种情况在学习C++时很少遇到)就必须要手动添加了。
原因三:没有将项目需要的头文件、源文件都加入项目中。这个经常会有人犯,可以进行排查。
避错方案:细心,另外就是在写程序时发现自己用到了新的类型就立刻把该类型的头文件添加(其实就是#include进来),如果写完某对象在其后加“.”后IDE环境不能智能弹出数据成员和成员函数,那么也要注意了,一般是因为你没有添加相应的头文件造成的。当然IDE环境出问题的情况也有,毕竟很少,区别对待即可。
原因四:当__declspec(dllexport)函数与inline函数混用并要生成.exe的工程也会偶然产生这种错误,这种错误我在最近做实验的时候遇到,目前具体原因不详,在新的工程中做写简单代码测试后也没有再产生link错误,最后解决的办法是去掉了inline(本身这个是建议性的语法,所以去掉后不影响编译),结果问题就解决了,也顺利通过了。
避错方案:在写exe工程时,不要写__declspec(dllexport)符号,本身这个符号是编译dll时使用的导出符号,符号用的多了难免冲突,在不明原因的情况下也很难排错。
原因五:当在MFC的导出DLL的类中,直接导出就会出现这种错误,必须在类前使用"__declspec(dllexport)"修饰导出类,这样再导时就能顺利通过了。
避错方案:在写MFC工程时,如果要导出DLL中的类或者是函数时,一定要在类名称前用"__declspec(dllexport)"符号加以声明或者在函数前进行相应的声明才能避免这种错误出现。