1

库的创建与使用(一)——静态链接库

在绝大多数时候,我们都不需要从头重新造轮子,直接使用别人封装好的库会让开发变得更高效、更易于管理复杂项目。本文将简单介绍静态链接库的基本技术。

为什么需要库

  1. “自己造轮子才能提高姿势水平”

    很好,如果有人跟你说这种话,那么请让他在不管用什么编程语言的时候都自己实现标准输入、标准输出功能;而且,一定要让他自己写出自己用的操作系统,让他自己写出BIOS芯片组的固件程序……这种人,怼之,使其滚之,敬而远之。

  2. “可以用头文件来代替库”

    在一个特定的项目里,可以用形形色色的头文件来管理类与函数的声明,但是局限性也很容易被看出来。首先,为了更有效的在之后的项目中使用头文件,那么自然会针对每一种类或函数去书写一份头文件,这就会导致头文件的数量变得很多;而且头文件是不参与编译的,为也能让项目编译通过,还要再把对应的实现源文件也引入到工程中。最重要的一点是保密性。头文件和源文件如果要给别人使用,那么别人都是可以直接修改源码的,尽管我本人是开源的支持者,但我们并不能保证每个人都是会遵守开源协议的。

  3. “写库太麻烦,对于少量的代码重用,直接复制源码操作简单”

    这点确实没错,但是用库的最大优势在于,库是编译过的,库只参与生成应用程序的链接过程——这也就是“链接库”这个名字的来历。

使用库的优点

以上总结了常见的对库持怀疑态度的人的看法,下面说一下为什么还是要推崇库。

  • 使用库,程序开发者可以不用去操心这个库是怎么实现的——并不是说程序开发者一定不会去实现那些他要用到的功能,只是在工程开发中,没有必要再去实现,因为写重复代码是最低效的开发方式——即使那些重复的部分具有相当高的技术含量。
    借助已有的库,我们可以把工作重心转移到更高级的功能开发上去,而不是整天抱着轮子去嘲讽跑车。

  • 既然我们会想到把很有技术含量而且还又是很多人都会用到的功能写成库以便再次使用,那么有商业头脑的人自然也能想得到。库是编译器编译过后的产物,对于C++来说,编译是不可逆的——就算是有逆向工程,那么不可能百分百还原,尚且逆向还都是靠有经验的程序员去猜编译前可能会是什么样子的。使用库我们就可以很好的隐藏自己的实现细节,而只把预留的接口给用户(指使用库的程序员)。
    如果哪天我们自己写出了一套相当具有商业价值的库,我们同样可以使用库的方式来发布、售卖我们的技术。

  • 因为库的编译过的,所以使用库而不是直接引入源代码在编译大型项目时会大大提高编译效率。

编写简单的静态库

一、静态库的基本概念

静态库是编译产生的二进制文件,在使用静态库的程序的最终编译过程中,链接器从库中复制这些函数与数据并把它们和应用程序的其他模块组合起来,生成可执行文件。库是不可以被执行的——或者说,库没有执行这个概念。
本文以Windows平台为例讲解,虽然中间步骤与Linux相差很大,但是学会了静态库的基础知识后,在其他平台的使用其实也换汤不换药。
因为静态库是编译过后的产物,所以用户是没法知道库中的实现信息的,但这也导致了一个问题:库中的函数和类用户也是不知道如何调用。为了绕开这个问题,我们可以利用C++中把声明与实现分离的技巧,给库配备一个面向用户的用于声明接口的头文件。下面我们先创建一个静态库:
Visual Studio 创建静态库向导

然后我们看一下这个静态库项目的属性:
静态库的项目属性

可以看到,编译后生成的将是一个.lib文件。

二、将函数包装成静态库

下面我们来看看把函数装进静态库以便实现代码重用。
先在项目中新建一个头文件:InitializeArray.h以及对应的源文件:InitializeArray.cpp,然后在头文件中加入我们要实现的函数的声明:
(代码格式问题,上图,凑合看吧)
头文件中的函数声明

然后在源文件中实现这个函数:
源文件中函数的实现

然后我们选择“生成StaticLibrary”(StaticLibrary是我这里使用的项目的名字),如图:
方法一:(先选中项目,再点击工具栏上的按钮)
方法一

方法二:(先选中项目,再使用菜单栏上的命令)
方法二

这样我们就得到了编译过后的静态链接库文件,在解决方案文件夹下的Debug(或者Release)文件夹中可以找到我们需要用到的静态库:
目标静态库文件

接下来我们测试一下编译生成的静态库,顺便看一下如何在程序中使用。在另一个项目中,我们需要将静态库文件引入到工程内,我这里将库拷贝到项目目录中的lib文件夹内:
拷贝静态库到工程中

我们还要把刚刚编写有函数声明的头文件一起放到工程目录下,我这里放到项目目录中的include文件夹内:
拷贝包含声明的头文件到工程中

有两种方法可以通知链接器在链接过程中将我们的静态库链接进来,下面分别给出:
方法一:在项目属性中设置链接器
在项目属性中设置链接器选项

方法二:在代码中显示地链接
在代码中显示地链接静态库

我个人比较喜欢使用代码进行控制,因为这种方式更直白,更容易看出程序使用了第三方的静态库。

然后我们就可以在用来测试的项目中编写测试代码来使用刚刚编译的静态库了:
测试函数的静态库的代码

运行效果如下:
函数静态库运行效果

三、将类包装成静态库

下面我们来试着把类装进静态库中。我们来实现一个整数类Integer

#include <vector>

#define VISIBLE_BEGIN
#define VISIBLE_END

class Integer
{
    VISIBLE_BEGIN
public:
    Integer();
    Integer(int integer);
    Integer(const Integer &);
    ~Integer();

    //判断整数是否为奇数
    bool IsOdd();

    //判断整数是否为偶数
    bool IsEven();

    //将整数分解质因数
    void Decomposition(std::vector<int> *pVec);
    //将整数逆序重组
    int Reverse();
    //取绝对值
    int Abs();

    //取两数较大者
    static int GetMaximum(int numberOne, int numberTwo);
    //取两数较小者
    static int GetMinimum(int numberOne, int numberTwo);
    //获取不大于整数的所有质数的集合
    static void GetPrimeArray(unsigned int integer, std::vector<int> *pResult);
    //取两数最大公因数
    static int GCD(unsigned int numberOne, unsigned int numberTwo);
    //取两数最小公倍数
    static int LCM(unsigned int numberOne, unsigned int numberTwo);

    //将整数隐示转换为int
    operator int();
private:
    bool m_bNegative;
    long m_lInteger;
    VISIBLE_END
};

关于这个类怎么实现,留给大家讨论。我直接把测试代码及其运行效果给出:

#include "./include/InitializeArray.h"
#include "./include/Integer.h"
#include <iostream>
using namespace std;

#pragma comment(lib, "./lib/StaticLibrary.lib")

int main()
{
    cout << "输入数组元素个数:";
    int size;
    cin >> size;
    int *arr;
    InitializeArray(arr, size);
    for (int i = 0; i < size; i++)
    {
        cout << arr[i] << " ";
    }
    cout << endl;

    cout << "输入一个整数:";
    cin >> size;
    Integer integer(size);
    cout << "输入另一个整数:";
    cin >> size;

    cout << integer << " 和 " << size << "的最小公倍数为 " << Integer::LCM(integer, size)
        << endl << "将" << integer << "分解质因数结果为:";

    vector<int> res;
    integer.Decomposition(&res);
    for (vector<int>::iterator iter = res.begin(); iter != res.end(); iter++)
    {
        cout << *iter << " ";
    }
    cout << endl;
    return 0;
}

别忘了把最新编译生成的静态库和头文件按之前的操作添加到工程目录中。

程序的运行效果如下:
加入类之后的测试程序运行结果

小结

静态库是完成代码重用的非常重要的一种手段,应用程序中往往都会大量使用静态库。本文演示的只是将C++中的一部分特性写进静态库,关于更多的技巧,请自行查阅更多资料。
当然,静态库也有很多缺点,而且,当库中的代码量很大但我们却不需要全部用到的时候,或者是我们想更灵活地使用库,那么这个时候我们就要用到使用更为复杂也支持更多特性的另一种库——动态链接库
关于动态链接库,下一篇文章将会介绍。


Soap
78 声望25 粉丝