动态链接库为模块化应用程序提供了一种方式,使得更新和重用程序更加方便,当几个应用程序在同一时间使用相同的函数时,它也帮助减少内存消耗,这是因为虽然每个应用程序有独立的数据拷贝,但是它们的代码是共享的。
C++ 库会涉及到三个东西: .h .lib .dll文件。
库的使用方式有三种:
1>. .h + .lib
2>. .dll
3>. .h + .lib + .dll
第三种方式是最常见的,第一种次之,第二种较少用。
(下面实例使用vs2013)
1>. .h + .lib
lib中存放了函数名及具体的函数实现代码,编译器链接时,链接lib文件到exe可执行程序中。
a. 创建库lib项目exampleLibFile: 新建项目,选择-->Win32 控制台应用程序,应用类型选择-->静态库 , 附加选项: 选择--> 空项目,取消预编译头和安全开发生命周期检查,(选择控制台应用程序在项目,在项目属性修改下 --常规--配置类型--中选择--静态库(.lib) 也可以创建lib项目)。
b. 创建库lib代码:
添加 .hpp 和 .cpp 文件( myLib.hpp myLib.cpp )
//.h 头文件
void go();
int add(int a, int b);
// cpp 文件
#include <stdio.h>
#include <windows.h>
void go() {
MessageBox(NULL, "我的来自lib的对话框", "Hello Lib", MB_OK);
}
int add(int a, int b) {
return a + b;
}
c. 编译生成 exampleLibFile.lib 文件。
d. 调用者项目中使用 myLib.hpp 和 exampleLibFile.lib 文件。
#include <stdio.h>
#include "myLib.hpp"
#pragma comment(lib,"../Debug/exampleLibFile.lib")
int main(void) {
go();
printf("来自lib的Add函数:%d\n", add(1, 2));
system("pause");
return 0;
}
2>. dll (使用 winapi调用)
dll中存放了函数名及具体的函数实现代码,运行时exe可执行程序通过winapi加载dll库文件。
a. 创建库dll项目exampleDllFile: 新建项目,选择-->Win32 控制台应用程序,应用类型选择-->静态库 , 附加选项: 选择--> 空项目,取消预编译头和安全开发生命周期检查,(选择控制台应用程序在项目,在项目属性修改下 --常规--配置类型--中选择--静态库(.lib) 也可以创建lib项目)。
b. 创建库dll代码:
添加 .hpp 和 .cpp 文件( classDll.hpp classDll.cpp 测试导出类, myDll.c测试导出函数)
//myDll.c
#include <stdio.h>
#include <windows.h>
//如果需要生成可供外部程序调用的函数,必须用dllexport
_declspec(dllexport) void go() {
MessageBox(NULL, "我是来自dll", "hello Dll", MB_OK);
}
//文件名为c 和 cpp, 生成的函数名字不同,因为C++有函数名重载机制,C没有
//cpp file
//void go(int a) {
//
//}
//
//void go(int a, double b) {
//
//}
//classDll.hpp文件
//导出类和导出函数一样 需要在前面加 __declspec(dllexport) 这是windows特有的
//库中定义的类和函数 会被两个项目使用 即原来dll项目和调用者项目
//所以要通过宏 预处理指令来实现库中定义的类和函数能被两个项目使用
//这里的.hpp 头文件会被分成两个文件 一个嵌入编译dll库文件中 另一个以.h文件方式供调用者使用
//在创建库项目时vs 编辑器会默认为库项目添加预处理宏: EXAMPLEDLLFILE_EXPORTS, (项目名_EXPORTS),这宏也可以手动添加。
//根据这个预处理宏来区别这个头文件是在dll库中使用还是在调用者项目中使用
#ifdef _WIN32 // WIN32知识 32位程序 _WIN64 _WIN32不区分32位和64
#ifdef EXAMPLEDLLFILE_EXPORTS //代表是库文件 dllexport: 导出
#define DLL_API __declspec(dllexport)
#else //代表调用者文件 dllimport: 导入
#define DLL_API __declspec(dllimport)
#endif
#else //linux 为空
#define DLL_API
#endif
class DLL_API ClassDll {
public:
ClassDll();
~ClassDll();
static int count;
};
// classDll.cpp文件
#include <iostream>
#include "classDll.hpp"
using namespace std;
int ClassDll::count = 0;
ClassDll::ClassDll() {
cout << "ClassDll Create" << endl;
}
ClassDll::~ClassDll() {
}
c. 编译生成 exampleDllFile.lib exampleDllFile.dll文件。
d. 调用者项目中使用 exampleDllFile.dll 文件。
//main.cpp
#include <stdio.h>
#include <windows.h>
//dll windows api调用方式
int main(void) {
HMODULE myDll = LoadLibrary("exampleDllFile1.dll");
if(NULL != myDll) {
GetProcAddress(myDll, "go")();
}
else {
printf("没有找到dll");
}
system("pause");
}
3>. .h + .lib + .dll
调用库的时候,要有两个文件 lib 和 dll, lib 中记录函数名及函数名对应的地址,dll中记录函数的实现代码。
a. 创建库dll项目exampleDllFile: 同 2>. dll (使用 winapi调用) 创建dll项目一样。
b. 创建库dll代码: 同 2>. dll (使用 winapi调用)创建库dll代码一样。
c. 编译生成 exampleDllFile.lib exampleDllFile.dll文件。(注意这里的lib文件虽然和第一种lib库文件后缀名相同,但包含的内容格式不一样,这地方lib只有函数名及对应地址, 文件往往就几k大小)
d. 调用者项目中使用 classDll.hpp exampleDllFile.lib exampleDllFile.dll 文件。
#include <iostream>
#include <windows.h>
#include "classDll.hpp"
using namespace std;
int main(int argc, char *argv[]) {
ClassDll cl;
cl.count++;
cout << "count = " << cl.count << endl;
getchar();
return 0;
}
在同一个解决方案中创建库和调用程序时,最好设置项目依赖关系 方便开发:
如: 设置项目b依赖项目a, 那么当编译项目b时, 要确保项目a是最新的,如果不是将先编译项目a。