C++中,API一般包括一个或多个头文件(.h)以及辅助文档。某个特定API的具体实现通常是可以链接到最终用户程序中的库文件。库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。库有两种:静态库(.a、.lib)和动态库(.so、.dll)。WIN32 API是纯C API,C++中API的杰出代表是STL。
静态库
在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接
静态库与汇编生成的目标文件一起链接为可执行文件,那么静态库必定跟.o文件格式相似。其实一个静态库可以简单看成是一组目标文件(.o/.obj文件)的集合,即很多目标文件经过压缩打包后形成的一个文件。静态库特点总结:
- 静态库对函数库的链接是放在编译时期完成的。
- 程序在运行时与函数库再无瓜葛,移植方便。
- 浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。
inux下使用ar工具、Windows下vs使用lib.exe,将目标文件压缩到一起,并且对其进行编号和索引,以便于查找和检索。一般创建静态库的步骤如图所示:
使用静态库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
//StaticMath.h #pragma once class StaticMath{ public: StaticMath(void); ~StaticMath(void); static double add(double a, double b);//加法 static double sub(double a, double b);//减法 static double mul(double a, double b);//乘法 static double div(double a, double b);//除法 void print(); }; //StaticMath.cpp #include "stdafx.h" #include "StaticMath.h" double StaticMath::add(double a, double b){ return a+b; } double StaticMath::sub(double a, double b){ return a-b; } double StaticMath::mul(double a, double b){ return a*b; } double StaticMath::div(double a, double b){ return a/b; } |
编写使用上面创建的静态库的测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include "StaticMath.h" #include <iostream> using namespace std; int main(int argc, char* argv[]) { double a = 10; double b = 2; cout << "a + b = " << StaticMath::add(a, b) << endl; cout << "a - b = " << StaticMath::sub(a, b) << endl; cout << "a * b = " << StaticMath::mul(a, b) << endl; cout << "a / b = " << StaticMath::div(a, b) << endl; StaticMath sm; sm.print(); system("pause"); return 0; } |
Linux下创建与使用静态库(待测试)
Linux静态库命名规则
Linux静态库命名规范,必须是”lib[your_library_name].a”:lib为前缀,中间是静态库名,扩展名为.a。
创建静态库(.a)
通过上面的流程可以知道,Linux创建静态库过程如下:
- 首先,将代码文件编译成目标文件.o(StaticMath.o)
1 |
g++ -c StaticMath.cpp |
注意带参数-c,否则直接编译为可执行文件
- 然后,通过ar工具将目标文件打包成.a静态库文件
1 |
ar -crv libstaticmath.a StaticMath.o |
生成静态库libstaticmath.a。
Linux下使用静态库,只需要在编译的时候,指定静态库的搜索路径(-L选项)、指定静态库名(不需要lib前缀和.a后缀,-l选项)。
1 |
# g++ TestStaticLibrary.cpp -L../StaticLibrary -lstaticmath |
- -L:表示要连接的库所在目录
- -l:指定链接时需要的动态库,编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.a或.so来确定库的名称。
Windows下创建与使用静态库
创建静态库(.lib)
1)如果是使用VS命令行生成静态库,也是分两个步骤来生成程序:
- 首先,通过使用带编译器选项 /c 的 Cl.exe 编译代码 (cl /c StaticMath.cpp),创建名为“StaticMath.obj”的目标文件。
- 然后,使用库管理器 Lib.exe 链接代码 (lib StaticMath.obj),创建静态库StaticMath.lib。
2)使用VS创建Win32 Project(不是Win32 Console Project),设置时选择静态链接库。Build项目即可生成静态库。注意是在Release模式下!
使用静态库
1)如果测试工程和静态库工程在一个Solution中,
- 工程“属性面板”→“通用属性”→“框架和引用”→”添加引用”,将显示“添加引用”对话框。 “项目”选项卡列出了当前解决方案中的各个项目以及可以引用的所有库。 在“项目”选项卡中,选择 StaticLibrary。
- 添加StaticMath.h 头文件目录,必须修改包含目录路径。打开工程“属性面板”→”配置属性”→“C/C++”→” 常规”,在“附加包含目录”属性值中,键入StaticMath.h 头文件所在目录的路径或浏览至该目录。
2)打开工程“属性面板”→”配置属性”→ “链接器”→ ”命令行”,输入静态库的完整路径即可。
3) “属性面板”→”配置属性”→“链接器”→”常规”,附加依赖库目录中输入,静态库所在目录;“属性面板”→”配置属性”→“链接器”→”输入”,附加依赖库中输入静态库名StaticMatch.lib。
动态库
静态库,容易使用和理解,也达到了代码复用的目的,那为什么还需要动态库呢?其实也是静态库的特点导致。
- 空间浪费是静态库的一个问题。
- 静态库对程序的更新、部署和发布页会带来麻烦。如果静态库liba.lib更新了,所以使用它的应用程序都需要重新编译、发布给用户(对于玩家来说,可能是一个很小的改动,却导致整个程序重新下载,全量更新)。
动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新。
动态库特点总结:
- 动态库把对一些库函数的链接载入推迟到程序运行的时期。
- 可以实现进程之间的资源共享。(因此动态库也称为共享库)
- 甚至可以真正做到链接载入完全由程序员在程序代码中控制(显示调用)。
Windows下创建与使用动态库
创建动态库(.dll)
使用VS创建Win32 Project(不是Win32 Console Project),设置时选择动态链接库。与Linux相比,在Windows系统下创建动态库需要一个DllMain函数做出初始化的入口;通常在导出函数的声明时需要有_declspec(dllexport)关键字。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
//DynamicMath.h #ifndef __DYNAMICMATH_H__ #define __DYNAMICMATH_H__ #pragma once #ifdef DYNAMICMATH_EXPORTS #define DYNAMICMATH_DECLSPEC __declspec(dllexport) #else #define DYNAMICMATH_DECLSPEC __declspec(dllimport) #endif class DynamicMath{ public: DYNAMICMATH_DECLSPEC DynamicMath(void); DYNAMICMATH_DECLSPEC ~DynamicMath(void); static DYNAMICMATH_DECLSPEC double add(double a, double b);//加法 static DYNAMICMATH_DECLSPEC double sub(double a, double b);//减法 static DYNAMICMATH_DECLSPEC double mul(double a, double b);//乘法 static DYNAMICMATH_DECLSPEC double div(double a, double b);//除法 __declspec(dllexport) void print(); }; #endif //DynamicMath.cpp #include "stdafx.h" #include "DynamicMath.h" DYNAMICMATH_DECLSPEC double DynamicMath::add(double a, double b){ return a+b; } DYNAMICMATH_DECLSPEC double DynamicMath::sub(double a, double b){ return a-b; } DYNAMICMATH_DECLSPEC double DynamicMath::mul(double a, double b){ return a*b; } DYNAMICMATH_DECLSPEC double DynamicMath::div(double a, double b){ return a/b; } |
使用动态库
创建win32控制台测试程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include "stdafx.h" #include "DynamicMath.h" #include <iostream> using namespace std; int _tmain(int argc, _TCHAR* argv[]){ double a = 10; double b = 2; cout << "a + b = " << DynamicMath::add(a, b) << endl; cout << "a - b = " << DynamicMath::sub(a, b) << endl; cout << "a * b = " << DynamicMath::mul(a, b) << endl; cout << "a / b = " << DynamicMath::div(a, b) << endl; system("pause"); return 0; } |
方法一: 如果测试工程和动态库库工程在一个Solution中,
- 工程“属性面板”→“通用属性”→“框架和引用”→”添加引用”,将显示“添加引用”对话框。 “项目”选项卡列出了当前解决方案中的各个项目以及可以引用的所有库。 在“项目”选项卡中,选择 DynamicMath。
- 添加DynamicMath.h 头文件目录,必须修改包含目录路径。打开工程“属性面板”→”配置属性”→“C/C++”→” 常规”,在“附加包含目录”属性值中,键入DynamicMath.h 头文件所在目录的路径或浏览至该目录。
- 将测试工程设置为Setup as StartUp Project.
方法二:
- 将DynamicMath.dll拷贝到该测试工程的Debug下。
- “属性面板”→”配置属性”→“链接器”→”常规”,附加依赖库目录中输入,动态库所在目录;D:\Projects\renlianshibie\DynamicMath\Debug;
- “属性面板”→”配置属性”→“链接器”→”输入”,附加依赖库中输入动态库编译出来的DynamicMath.lib。
无论是静态链接库还是动态链接库,最后都有lib文件,那么两者区别是什么呢?其实,两个是完全不一样的东西。
静态库对应的lib文件叫静态库,动态库对应的lib文件叫【导入库】。实际上静态库本身就包含了实际执行代码、符号表等等,而对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息。
大致就写到这里,Linux下调用还需要测试。以及显示调用等以后再写吧。