1, 静态库与动态库的区别:
最显著的区别是: 静态库 生成EXE文件后可以遗弃, 动态库却要一直跟随。
静态库的原理是: 在编译链接可执行文件(exe)时, 链接器将库中的函数与数据 与应用程序的其他模块组合起来, (这里我看作是inline 或是 insert)
动态库的原理是: 首先动态库也有类似静态库的文件(lib)(确实让人费解, 这里我们称之引入库文件, 不知大家记得函数申明吗? 所谓的申明在h文件, 实现在cpp文件?这里就是答案了。 这里的引入库文件充当的就是h文件的角色。在编译的时候跟静态库的原理相似, lib文件组合入应用程序中, 而dll仍在外部。这也是为什么生成EXE文件后动态库一直跟随的原因)
其实在上面已经道出动态链接库的优势所在。。。。(plug and play?)当我们想要更改程序里面的某一个函数的实现, easy, 只需改变其dll。还有, 这里就意味着分工合作实现的可能性, 每个人只需为自己的dll文件负责。。。更多详细内容,请百度, 这里就不多累赘了。
2, 简单制作dll && 使用之:
制作dll,
本人用的是VS2013, 新建 控制台项目 下一步 选择dll 空项目, 下面是其代码:
MyDll.cpp
int sharenum = 0;
__declspec(dllexport) int add(int a, int b)
{
return a + b;
}
__declspec(dllexport) double add(double a, double b)
{
return a + b;
}
__declspec(dllexport) int minus(int a, int b)
{
return a - b;
}
__declspec(dllexport) int shareNum()
{
return sharenum;
}
__declspec(dllexport) int sharenumplus(int num)
{
sharenum += num;
return shareNum();
}
使用dll:
正如我上面(第一节所言, 编译需要lib 以及dll文件, 在本工程Debug中, 复制其lib dll。)下面是使用的代码:
testDll.cpp
#include "stdio.h"
#pragma comment (lib, "ConsoleApplication1.lib")
_declspec(dllimport) int add(int a, int b);
extern int minus(int a, int b);
extern int shareNum();
extern int sharenumplus(int num);
int main()
{
printf("1 + 2 = %d\n", add(1, 2));
printf("1 - 2 = %d\n", minus(1, 2));
printf("shareNum is %d\n", shareNum());
char ch;
scanf("%c", &ch);
printf("shareNum + 1 is %d\n", sharenumplus(1));
scanf("%c", &ch);
return 0;
}
温馨提示:
注1: ConsoleApplication1.lib 因为我上一个工程的名称为之,这里值得注意的是 _declspec(dllimport) 与 extern (顺道说一下上文的_declspec(dllexport), 它表示该方法可以被其他程序所调用,maybe private ->>> public?), 回到正题, 这俩者的效果是一致的, 也就是声明外部调用的方法, 不同的是:_declspec(dllimport)更高效。注2:更为常见的是:头文件定义好所有函数, 源文件实现, 这里用一个技巧:
//h文件:
#ifndef DLL1_API
#define DLL1_API _declspec(dllimport)
#endif // !DLL1_API
DLL1_API int add(int a, int b);
。。。。。。
//cpp:
#define DLL1_API _declspec(dllexport)
#include "dllHeader.h"
int add(int a, int b)
{
return a + b;
}
。。。
这里有个好处, 即:可以有一个类似doc的文件(h文件)可以参考,方便使用者。 当然最显著的特点是, 使用dll的时候, 只需加载lib, 函数申明可以用#include头文件解决。
#include "dllHeader.h"
#include "stdio.h"
#pragma comment (lib, "MyDll.lib")
int main()
{
printf("1 + 2 = %d\n", add(1,2));
char ch;
scanf("%c", &ch);
}
3, 从DLL中导出类:
基本格式:
//**.h
class DLL1_API robot{
public :
void sayhello();
};
//**.cpp
void robot::sayhello()
{
printf("hello world");
}
//关于DLL1_API, 请看上文温馨2
不知读者是否知道 transient, 这个是在序列化里面的(Java), 所修饰的数据将不参与序列化过程。
而这里的_declspec(export) 是否有异曲同工之妙呢? 只有用这个修饰的才能从dll 中导出。
4. 解决名字改编问题:
问题描述:
编译器在生成dll文件的时候, 会对其名字进行改编, 不同编译器, 改编的形式或许有所不同, 这就有可能发生A编译生成的dll, B不能使用的情况。
(2015/04/22 其原因是:C 中没有重载的概念,而C++有,也就是说,C++编译生成名字的时候,会加上其参数列表的内容,测试代码如下:)
//override.c
#include "stdio.h"
int hello(int a)
{
printf("arg is %d\n", a);
return 0;
}
int hello()
{
printf("no arg");
return 0;
}
int main()
{
return 0;
}
结果:
插播一下(其实命名很重要,一个好的文件,函数,字段名字是无需额外的注解的,所以学好英语很重要 ovo 请忽略我,我一般百度翻译。。)
解决方法:
方法一:
//h文件:
#ifndef DLL1_API
#define DLL1_API <strong>extern "C" </strong>_declspec(dllimport)
#endif // !DLL1_API
DLL1_API int <strong>_stdcall</strong> add(int a, int b);
。。。。。。
//cpp:
#define DLL1_API<strong> <span style="font-family: Arial, Helvetica, sans-serif;">extern "C"</span></strong><span style="font-family: Arial, Helvetica, sans-serif;"><strong> </strong>_declspec(dllexport)</span>
#include "dllHeader.h"
int <strong>_stdcall</strong> add(int a, int b)
{
return a + b;
}
。。。
方法二: 使用模块定义文件DEF
LIBRARY MyDll
EXPORTS
add //sample1
myminus= minus //sample2
sample1: 表示源文件中定义的add 将以 add 符号名导出。
<strong>1 0 00011109 add = @ILT+260(_add@8)
2 1 00011005 minus = @ILT+0(_minus)
3 2 00011005 myminus = @ILT+0(_minus)</strong>
结语:
这里是我的一人之言, 总有疏漏, 欢迎打脸。。。