基本概念
程序编译一般需经预处理、编译、 汇编和链接几个步骤。在我们的应用中,有一些公共代码是需要反复使用,就把这些代码编译为“库”文件。
静态库:在链接步骤中,连接器将从库文件取得所需的代码,复制到生成的 可执行文件中。这种库称为静态库,其特点是 可执行文件中包含了库代码的一份完整拷贝;缺点就是被多次使用就会有多份冗余拷贝。
动态库:DLL 是一个包含可由多个程序同时使用的代码和数据的库。Windows下动态库为.dll后缀,在linux在为.so后缀。
在VS中新建生成静态库的工程,编译生成成功后,只产生一个.lib文件;新建生成动态库的工程,编译成功后,产生一个.lib文件和一个.dll文件。既然都产生lib文件,那它们有什么区别呢?
静态库中的lib:包含函数代码本身(即包括函数的索引,也包括实现),在编译时直接将代码加入程序当中。
动态库中的lib:包含了函数所在的DLL文件和文件中函数位置的信息(索引),函数实现代码由运行时加载在进程空间中的DLL提供。
总之,lib是编译时用到的,dll是运行时用到的。如果要完成源代码的编译,只需要lib;如果要使动态链接的程序运行起来,只需要dll。
如何生成动态库
本人使用的是VS2010,步骤如下:
① 新建项目–win32项目–填写项目名–确定–下一步–应用程序类型:选择DLL–附加选项:选择导出符号–完成
②可以看到生成了一个dllmain.cpp 文件,这是dll应用程序的入口,注意它和普通工程的入口main函数不同,这个文件我们不需要修改。
在这个动态库中我们举例导出一个变量(int nMyDLL),一个类(CMyDLL),一个函数(int fnMyDLL(void))。
新建生成dll的工程时,vs默认定义了宏MYDLL_EXPORT(在预处理器定义中可以看到,下图所示), 因此,MYDLL_EXPORT是 __declspec(dllexport),用来导出。当我们在静态调用dll时,我们包含该头文件,此时由于没有定义MYDLL_EXPORT,所以MYDLL_EXPORT是__declspec(dllimport),用来导入。可以发现,使用这种方式进行宏定义,一举两得,代码复用同时又降低了维护成本。如果我们想同时实现_32位和_64位程序、Debug版本和Release版本 都可以采用这种方式。
③在定义类时,在类名前加上这个宏,来表明该类是导出类还是导入类。最后一行导出函数还可以写成:
extern "C" MYDLL_API int fnMyDLL(void);
extern “C” 是为了保证编译时生成的函数名不变,这样动态调用dll时才能正确获取函数的地址。
下图是MyDLL.cpp
如何调用动态库
调用动态库有两种方法:隐式链接和显示链接。
调用动态库:隐式链接
隐式链接 需要.h文件,dll文件,lib文件
(1)将dll放到工程的工作目录
(2)设置项目属性–vc++目录–库目录为lib所在的路径
(3)将lib添加到项目属性–链接器–输入–附加依赖项(或者直接在源代码中加入#pragma comment(lib, “**.lib”))
(4)在源文件中添加.h头文件
然后就像平常一样调用普通函数、类、变量
调用动态库:显示链接
显示链接 只需要.dll文件,但是这种调用方式不能调用dll中的变量或者类(你想想,连头文件都没有,怎么调用?)。
显示调用主要使用WIN32 API函数LoadLibrary、GetProcAddress,例如:
typedef int (*dllfun)(void);//定义指向dll中函数的函数指针类型
HINSTANCE hlib = LoadLibrary(".\\MyDLL.dll");
if(!hlib)
{
std::cout<<"load dll error\n";
return -1;
}
dllfun fun = (dllfun)GetProcAddress(hlib,"fnMyDLL");//定义函数指针fun
if(!fun)
{
std::cout<<"load fun error\n";
return -1;
}
fun();
生成静态库
相比动态库,静态库的生成和调用就简单多了。
①新建项目–win32项目–填写项目名–确定–下一步–应用程序类型:选择静态库。
②静态库项目没有main函数,也没有像dll项目中的dllmain。
创建项目后添加.h文件,添加相应的导出函数、变量或类,如下所示
#ifndef _MYLIB_H_
#define _MYLIB_H_
void fun(int a);
extern int k;
class testclass
{
public:
testclass();
void print();
};
#endif
cpp文件
#include "stdafx.h"
#include "lib.h"
#include <iostream>
void fun(int a)
{
std::cout<<a<<"lib gen\n";
}
int k = 42;
testclass::testclass()
{
std::cout<<"123\n";
}
void testclass::print()
{
std::cout<<"this is testcalss\n";
}
使用静态库
需要.h文件,lib文件
(1)设置项目属性–vc++目录–库目录为lib所在的路径
(2)将lib添加到项目属性–链接器–输入–附加依赖项(或者直接在源代码中加入#pragma comment(lib, “**.lib”))
(3)在源文件中添加.h头文件
然后就像平常一样调用普通函数、类、变量,举例如下:
#include <iostream>
#include "lib.h"
#pragma comment(lib, "lib.lib")
int main()
{
fun(4);
std::cout<<k<<std::endl;
testclass tc;
tc.print();
return 0;
}