C++实习笔记(3)

来源:互联网 发布:c语言中int的取值范围 编辑:程序博客网 时间:2024/06/07 18:41

1.dll库和lib库的区别

一般来说,dll库是动态链接库,相当于写好的一堆代码(其实就是exe,但是没有main函数),lib则是静态链接库。
所谓静态连接就是把函数或过程直接连接到可执行文件中,成为可执行文件中的一部分。当多个程序调用同样的函数时,内存里则会有该函数的多份拷贝,浪费内存资源。
所谓动态链接则是提供了一个函数的描述信息给可执行文件(此时并没有内存的拷贝),当程序运行时系统会在底层创建应用程序和dll之间的关系,当需要用到dll中的内容时则查找dll并连接dll库。

**dll是在程序运行阶段才需要的文件,而lib是程序在编译时则需要链接的文件**

另外需要注意的是lib库分为两种:一种是静态编译产生的lib,体积比较大,包含函数索引以及实现。另一种lib库则是在生成dll时产生的lib(引导库),体积比较小,只有函数索引,没有函数实现。
但是不管哪一种lib,其编译时都按照lib的静态链接的方式进行编译。
lib库、.h文件都是编译的时候已经链接到运行程序中了,但是.h文件只有函数的申明,函数具体的实现一般则是在dll库中实现,因此版本更新的时候要注意把别人生成的第三方库一起手动拷贝过来并替换
例如,有第三方库FeatureDetectionAPI,当只把FeatureDetectionAPI.h的头文件复制到工程目录下的时候,程序编译不会通过,有可能会报错:GetFDVersion不是FeatureDetectionAPI中的成员(GetFDVersion是新加入的函数)。这是因为此时虽然.h文件更新了,但是dll库还是之前的老库,所以在老库中是没有这个函数的实现的,所以在更新的时候还需要手动把第三方的dll拷贝过来。

两种出现error LNK2001: 无法解析的外部符号 “int Xiao” (?Xiao@@3HA)的情况

情况1:因为c++与c代码的不同而导致所写的代码不被编译器识别。
参考:c++与c代码的不同导致错误
一般这种情况在于编译器无法识别是C++代码还是C代码,所以解决办法就是将C代码放在

    extern "C" {...}

里,更多修改可以查看参考链接。
需要注意的是,此种情况的错误往往会有 int Xiao” (?Xiao@@3HA) 类似的比较看不懂的符号作为错误提示。这是因为C++编译器无法识别C代码。

情况2:使用动态链接库dll时,添加新接口后要输出到dll中才能被识别,不然会显示
error LNK2001: 无法解析的外部符号 _7_CALL_initCamera
error LNK2001: 无法解析的外部符号 _7_CALL_setBrightness
error LNK2001: 无法解析的外部符号 _7_CALL_setExposure
error LNK2001: 无法解析的外部符号 _7_CALL_setVideoGain
error LNK2001: 无法解析的外部符号 _7_CALL_ReleaseCamera
注意此时的错误,是没有奇怪的符号的,说明并不是因为C++无法识别C代码产生的错误,因为函数名已经被正确识别,只是因为没有并定义,所以无法识别。
注意此时有两种办法可以将函数输出(export)到dll库中,第一种就是直接在函数头前加上__declspec(dllexport) 将函数导出为Dll中供其它程序使用,例如:

_declspec(dllexport) int add(int a, int b);

如果调用该函数的同样是C++(同一个编译器版本),那么将不会有问题。但是如果此时需要将代码移植到其他例如C#平台,则可能会出现情况1的错误:因为编译器不同,会编译出不同的函数名,从而导致函数找不到无法识别。例如在c编译器中则需要写成:

extern "C" _declspec(dllexport) int add(int a, int b);

其他平台则也会不一样。
但是此时我们可以利用def文件来实现更简单的导出。
情况2:使用def文件导出函数到dll
首先在项目中右键添加一个def文件(系统会自动在连接器->输入->模块定义文件中加入该def文件),然后将每个需要导出的函数写入def文件即可。
这是一个def文件的写入模板:

; _7_UserSDK.def : 声明 DLL 的模块参数。LIBRARY "USERSDK.dll"EXPORTS    ; 此处可以是显式导出    _7_CALL_device_connect @1    _7_CALL_device_disconnect @2    _7_CALL_device_register_request @3    _7_CALL_device_register @4    _7_CALL_init @5    _7_CALL_release @6

3.编译器的配置(宏路径等)

不同项目都可以设为启动项,例如在解决方案中有用于测试的项目:USERTEST
有用于出dll库的SDK项目:USERSDK
现在需要将USERSDK设为启动项目,以启动程序(exe程序)进行实际测试,则需要项目->属性->调试中的“命令”设为需要启动的exe程序路径;
将“命令”中的工作目录设为该exe程序所在的目录。如下图:
设置启动项目图示
如果需要使用USERTEST进行测试,则需要设置USERTEST为启动项目,同时需要注意此时的工作目录一般不能是ProjectDir(ProjectDir),则表示是程序项目所在的文件夹,一般这个文件夹并不包含debug或者release需要的配置文件等。例如在编译dll时我们将所需要的配置文件、data数据等都放在bin->x86->P3_Release_CPU文件夹下(也就是文件的输出目录,编译好的exe、dll等文件都会放在那个目录下),则需要将此时的工作目录设置为$(OutDir)——–也就是输出目录!

关于链接器的设置
如果USERSDK项目需要用到附加库,则在属性->链接器->附加库目录中,将dll文件所在的路径写在附加库目录下,如图:
附加dll库

如果FD_Gaze库需要附加头文件目录,则可以在属性->VC++目录->包含目录中将.h文件所在文件夹路径写在包含文件路径下,如图:
这里写图片描述

注意路径宏中的下划线应该是 ‘/’下划线 而不是’\’

配置新的 解决方案配置

可以在编译器中配置新的解决方案配置,同时可以选择以某个设置好的配置为模板直接复制,但是需要注意的是当切换解决方案配置的时候,注意项目是否与解决方案配置同时切换过来,编译的时候最好确认一遍。
记一件比较狗血的案例就是当我配置了一个新的配置方案FG_Test,编译的时候程序自动生成了一个文件夹FG_Tset文件夹保存生成的dll等文件。另一个同事也创建了一个新的FG_test配置方案并上传到svn,然后我revert在通过svn更新代码过后,我的配置也变成了FG_test。
但是当我编译的时候,并没有生成新的文件夹FG_test,新编译的dll库也存入了原来的FG_Test文件夹,然后我运行程序却发现报错(莫名错误),找了很久才发现是我的工作目录就是$(OutDir),使用的配置文件是之前我自己配置的FG_Test中的配置文件,从而导致运行错误。
原因应该是Windows对于文件夹目录不区分大小写,系统误认为FG_Test文件夹就是FG_test文件夹了。

函数重载(OverLoad) 和函数重写(OverRide)的区别

函数重载(OverLoad)指的是同一可访问区内被声明的几个具有不同参数列(参数的类型、个数、顺序不同)的同名函数,根据参数列表可以确定调用那个函数,重载不关心函数的返回类型。
函数重写(OverRide)指的是在派生类中存在重新定义的函数,其函数名、参数列表、返回值类型所有都必须同基类中被重写的函数一致。只有花括号内的函数体不同,派生类调用时会调用派生类的重写函数,不会调用被重写函数,重写的基类中被重写的函数必须有vitural修饰。
二者区别
1.范围区别:重载和被重载的函数是在同一类中的,重写和被重写函数是在不同类中的。
2.参数区别:重载和被重载的函数参数列表一定不同,重写和被重写的函数参数列表一定相同。
3.virtural的区别:重载函数和被重载函数可以被vitural修饰,也可以没有;但是重写的基类函数必须要有virtual修饰。

原创粉丝点击