1. 程式人生 > >Qt 之建立並使用靜態連結庫

Qt 之建立並使用靜態連結庫

簡述

摘自維基百科:

靜態連結庫(Statically-linked library),或稱靜態庫,是一個外部函式與變數的集合體。靜態庫的檔案內容,通常包含一堆程式設計師自定的變數與函式,其內容不像動態連結庫那麼複雜,在編譯期間由編譯器與聯結器將它整合至應用程式內,並製作成目標檔案以及可以獨立運作的可執行檔案。而這個可執行檔案與編譯可執行檔案的程式,都是一種程式的靜態建立(static build)。以過去的觀點來說,庫只能算是靜態(static)型別。

|

建立靜態連結庫

建立靜態連結庫的步驟和共享庫基本相同,唯一的區別是【型別】需要選擇“靜態連結庫”:

這裡寫圖片描述

這裡,將專案名稱命名為 StaticLib

專案檔案

在專案建立完成之後,Qt Creator 會自動生成相關的檔案,目錄結構如下:

這裡寫圖片描述

與共享庫不同,這裡並沒有出現了一個名為 {projectName}_global.h 的檔案,Why?這是因為靜態連結庫將被內建到應用程式中,所以無需在 .h 檔案中設定匯出和匯入符號的特殊功能。

檢視 .pro,並對其稍作修改:

TARGET = StaticLib

# 新增部分
CONFIG += debug_and_release
CONFIG(debug, debug|release) {
    unix: TARGET = $$join(TARGET,,,_debug)
    else:
TARGET = $$join(TARGET,,,d) } TEMPLATE = lib CONFIG += staticlib

與共享庫的另一個不同之處在於:這裡多了一個 CONFIG 選項,其值為 staticlib。沒錯,這正是要告訴 qmake,我們需要生成的是一個靜態連結庫。

生成靜態連結庫

可以看到,生成的庫中只有一個簡單的類定義,對其稍作修改。

staticlib.h 內容如下:

#ifndef STATICLIB_H
#define STATICLIB_H

int subtract(int x, int y);

class StaticLib
{
public
: StaticLib(); int add(int x, int y); }; #endif // STATICLIB_H

staticlib.cpp 內容如下:

#include "staticlib.h"

int subtract(int x, int y)
{
    return x - y;
}

StaticLib::StaticLib()
{
}

int StaticLib::add(int x, int y)
{
    return x + y;
}

構建(不執行)專案,會生成相應的 .lib 檔案。

注意: Debug 版本為 StaticLibd.lib(帶 d),Release 版本為 StaticLib.lib(不帶 d)。

使用靜態連結庫

建立一個簡單的客戶端 - Qt Console Application,並使用靜態連結庫,效果如下:

這裡寫圖片描述

專案建立成功後,將剛才生成的靜態連結庫組織成以下結構:

  • StaticLibClient/
    • StaticLibClient.pro
    • main.cpp
    • 3rdparty/
      • StaticLib/
        • include/
          • staticlib.h
        • lib/
          • StaticLibd.lib
          • StaticLib.lib

靜態連結庫的使用方式和共享庫類似,不同的是這裡將【連結】選擇為“靜態”:

這裡寫圖片描述

這時,.pro 中會自動新增以下程式碼:

win32:CONFIG(release, debug|release): LIBS += -L$$PWD/3rdparty/StaticLib/lib/ -lStaticLib
else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/3rdparty/StaticLib/lib/ -lStaticLibd

INCLUDEPATH += $$PWD/3rdparty/StaticLib/include
DEPENDPATH += $$PWD/3rdparty/StaticLib/include

win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$PWD/3rdparty/StaticLib/lib/libStaticLib.a
else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$PWD/3rdparty/StaticLib/lib/libStaticLibd.a
else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$PWD/3rdparty/StaticLib/lib/StaticLib.lib
else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$PWD/3rdparty/StaticLib/lib/StaticLibd.lib

類似於為共享庫載入所做的工作,需要將 INCLUDEPATH 指向標頭檔案所在目錄,並將 LIBS 變數指向 .lib 檔案。但是,這裡多了一個變數 - PRE_TARGETDEPS,它用於列出目標所依賴的庫,尤其是對於顯式列出相關的靜態庫很有用。

建議: 如果使用靜態連結庫,應該始終同時使用 LIBSPRE_TARGETDEPS

開始測試,main.cpp 內容如下:

#include <QCoreApplication>
#include <qDebug>
#include "staticlib.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // 測試庫
    StaticLib lib;
    qDebug() << lib.add(2, 3);

    qDebug() << subtract(5, 2);

    return a.exec();
}

OK,執行程式,效果如上所示。

選擇哪種方式

目前為止,介紹了兩種庫 - 共享庫和靜態連結庫,但是分享了三種使用方式。

到底如何選擇?這完全取決於你的需要。

  • 在建立共享庫時,需要將其部署到應用程式中。從好的方面來說,與共享庫相關聯的應用程式和庫很小。
  • 是否要使用 QLibrary 在執行時載入 .dll,取決於是否訪問了 .h 標頭檔案和 .lib 檔案。如果無法訪問這些檔案,那麼 QLibrary 是另一種選擇。
  • 靜態連結會生成一個獨立的可執行檔案,這樣做的好處是隻需要部署幾個檔案,缺點是可執行檔案很大。