帶你玩轉Visual Studio——帶你發佈自己的工程庫

原文鏈接:http://blog.csdn.net/luoweifu/article/details/48895765

上一篇文章帶你玩轉Visual Studio——帶你高效管理代碼通過對VisualSVN優秀插件的講解,讓我們掌握了在集成開發環境VS中快捷高效地管理代碼的技能。然而我們開發的程序並不總是直接地生成可執行的軟件,我們可能只是開發某個大型系統的一個組件,也可能是開發某個軟件的內核SDK提供給上層的應用程序調用,在開發的過程中我們也可能會用到第三方的開源庫。那如果將自己的程序編譯成程序庫給調用方用呢?又如何在自己的程序中引用第三方庫呢?這將是這篇文章要講的內容——發佈自己的工程庫。

什麼是程序庫?

庫是寫好的現有的,成熟的,可以複用的代碼。現實中每個程序都要依賴很多基礎的底層庫,不可能每個人的代碼都從零開始,因此庫的存在意義非同尋常。比如你經常使用的STL(Standard Template Library)也是庫,有了STL你才能方便地使用std::string、std::cout這些類。

本質上來說庫是一種可執行代碼的二進制形式,可以被操作系統載入內存,被別的程序調用執行。C++的庫有兩種:靜態庫和動態庫。將一個程序編譯成可執行文件一般經過 預編譯–>編譯–>鏈接 這幾個過程,而靜態庫與動態庫的區別主要體現在鏈接這個過程。

靜態庫:

在鏈接階段,會將編譯的目標文件.obj 與引用到的庫.lib 一起鏈接打包到可執行文件exe(也稱爲目標代碼)中,程序運行時將不再需要該靜態庫。

因此最終鏈接成的可執行文件(.exe)體積較大。在Windows中一般以.lib爲後綴名,在Linux中一般以.a爲後綴名。

動態庫:

在鏈接階段,動態庫.dll並沒有真正被連接到目標代碼中,只是將這個動態庫的聲明鏈接到目標代碼中(這樣程序運行時才知道怎樣使用這個動態庫),動態庫.dll依然是獨立存在的,只有在程序運行是纔會將.dll載入到內存中被程序調用。因此程序運行時必須要有這個動態庫且放在正確的路徑中。

因此最終鏈接成的可執行文件(.exe)體積較小。在Windows中一般以.dll爲後綴名,在Linux中一般以.so爲後綴名。

靜態庫與動態庫的區別:

特點 靜態庫 動態庫
對函數庫的鏈接時機 在編譯的鏈接階段完成的 推遲到程序運行的時期
運行過程與庫的關係 程序在運行時與靜態庫再無瓜葛 程序在運行時與動態庫庫需要一直存在且路徑正確
是否鏈接到可執行文件 靜態庫被鏈接合成一個可執行文件 動態庫不會被鏈接到可執行文件中
目標文件大小 體積較大 體積較小
內存佔用度 佔用內存。如果多個程序使用了同一個靜態庫,每一個程序者會包含這個靜態庫 節約內存。如果多個程序使用了同一個動態庫,可以實現進程之間的資源共享(因此動態庫也稱爲共享庫)
程序移植 移植方便 移植不太方便,需要所有動態庫的頭文件
程序升級 程序升級麻煩,需要下載整個程序進行升級 程序升級更簡單,只需要升級某個DLL或某個程序,下載一個升級包即可
編譯出的結果文件 ProjectName.lib ProjectName.lib+ProjectName.dll, 這裏的ProjectName.lib與靜態庫的.lib文件不同,這只是一個導入庫,只包含了地址符號表等,以便調用方的程序能找到對應的函數,真正的庫文件是ProjectName.dll


編譯自己的工程庫

假設我們有這樣一個工程,這個工程的作用就是提供一些常用的工具類和方法,然後我們要將這個工程編譯成庫提供給別人使用。

編譯靜態庫

假設我們已經建好工程並寫好了相應的代碼: 
工程目錄 
工程目錄

Utils.h:

//===============================================================
//Summary:
//          Utils 類, 工具類
//FileName:
//          Utils.h
//Remarks:
//          ...
//Date:
//          2015/10/4
//Author:
//          Administrator([email protected])
//===============================================================

#ifndef __UTILS_H__
#define __UTILS_H__

#include <string>
#include <strstream>
//#include <cstdlib>

class Utils
{
public:
    Utils(void);
    ~Utils(void);

public:
    //---------------------------------------------------------------
    //function: 
    //          WString2String wstring 到 string 的轉換
    //Access:
    //           public  
    //Parameter:
    //          [in] const std::wstring & ws - wstring字符串
    //Returns:
    //          std::string - string字符串
    //Remarks:
    //          些方法跨平臺,可移植版本
    //author:   luoweifu
    //---------------------------------------------------------------
    static std::string WString2String(const std::wstring& ws);

    //---------------------------------------------------------------
    //function: 
    //          String2WString string 到 wstring 的轉換
    //Access:
    //           public  
    //Parameter:
    //          [in] const std::string & s - string 字符串
    //Returns:
    //          std::wstring - wstring字符串
    //Remarks:
    //          些方法跨平臺,可移植版本
    //author:    luoweifu
    //---------------------------------------------------------------
    static std::wstring String2WString(const std::string& s);

};

//---------------------------------------------------------------
//function: 
//          ConvertToString 將int轉換成string
//Parameter:
//          [in] int val - 要轉換的變量
//Returns:
//          std::string - 轉換後的字符串
//Remarks:
//          ...
//author:   luoweifu
//---------------------------------------------------------------
std::string ConvertToString(int val);

#endif  //__UTILS_H__
  • 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
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 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
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73

上述聲明的實現參考後面的附錄Utils.cpp。 這裏的註釋是通過VAssistX生成的,關於VAssistX的用法可參考前面寫的一篇文章帶你玩轉Visual Studio——帶你高效開發

要編譯成靜態庫,我們可以這樣設置我們的工程: 
右鍵工程->Properties 

編譯成靜態庫 
編譯成靜態庫

然後右鍵Build就可以了,你可以在解決方案下的Debug(實際的情況中一般要編譯成Release版本,設置的方法一樣,這裏的內容後一章中再講)目錄下就能看到Utils.lib,這就是編譯出的庫。要將這個庫給別人使用,只要提供這個Utils.lib和這個工程的頭文件就可以。將Utils.h拷貝到D:\ReleaseLibs\StaticLib\Includes,將Utils.lib拷貝到D:\ReleaseLibs\StaticLib\Libs,把D:\ReleaseLibs\StaticLib這個文件提供出去就可以了。靜態庫的使用請看後一小節使用靜態庫

編譯動態庫

與靜態庫相比,編譯動態庫要麻煩一些,一般要在導出函數的聲明處加上_declspec(dllexport)關鍵字前綴。 
1. *Utils.h的聲明如下

//===============================================================
//Summary:
//          Utils 類, 工具類
//FileName:
//          Utils.h
//Remarks:
//          ...
//Date:
//          2015/10/4
//Author:
//          Administrator([email protected])
//===============================================================

#ifndef __UTILS_H__
#define __UTILS_H__

#include <string>
#include <strstream>
//#include <cstdlib>

//===============================================================

//===============================================================

class Utils
{
public:
    Utils(void);
    ~Utils(void);

public:
    //---------------------------------------------------------------
    //function: 
    //          Max 獲得兩個數中的最大值
    //Access:
    //           public  
    //Parameter:
    //          [in] int nValue1 - 第一個數
    //          [in] int nValue2 - 每二個數
    //Returns:
    //          int - 最大值
    //Remarks:
    //          ...
    //author:   luoweifu
    //---------------------------------------------------------------
    static int Max(int nValue1, int nValue2);

    //---------------------------------------------------------------
    //function: 
    //          Min 獲得兩個數中的最小值
    //Access:
    //           public  
    //Parameter:
    //          [in] int nValue1 - 第一個值
    //          [in] int nValue2 - 第二個值
    //Returns:
    //          int - 最小值
    //Remarks:
    //          ...
    //author:   luoweifu
    //---------------------------------------------------------------
    static int Min(int nValue1, int nValue2);

    //---------------------------------------------------------------
    //function: 
    //          Range 將一值限定在一個範圍內
    //Access:
    //           public  
    //Parameter:
    //          [in] int nMin - 最小值
    //          [in] int nMax - 最大值
    //Returns:
    //          int - 返回在限制在該範圍內的一個值
    //Remarks:
    //          ...
    //author:   luoweifu
    //---------------------------------------------------------------
    static int Range(int nMin, int nMax, int nValue);
};


//---------------------------------------------------------------
//function: 
//          ConvertToInt 將一個常量字符串轉換成int類型數據
//Access:
//           public  
//Parameter:
//          [in] const char * pStr - 常量字符串
//Returns:
//          int - 轉換成的int值
//Remarks:
//          ...
//author:   luoweifu
//---------------------------------------------------------------
int ConvertToInt(const char* pStr);

#endif  //__UTILS_H__
  • 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
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 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
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  1. 要編譯成動態庫,我們可以這樣設置我們的工程: 
    右鍵工程->Properties 
    設置編譯的目標類型
    設置編譯的目標類型

    設置預編譯宏
    設置預編譯宏

然後右鍵Build就可以了,你可以在解決方案下的Debug(實際的情況中一般要編譯成Release版本,設置的方法一樣,這裏的內容後一章中再講)目錄下就能看到Utils.dll和Utils.lib,這就是編譯出的庫。要將這個庫給別人使用,只要提供這個Utils.dll、Utils.lib和這個工程的頭文件就可以。將Utils.h拷貝到D:\ReleaseLibs\DynamicLib\Includes,將Utils.dll和Utils.lib拷貝到D:\ReleaseLibs\DynamicLib\Libs,把D:\ReleaseLibs\DynamicLib這個文件提供出去就可以了。靜態庫的使用請看後一小節使用動態庫

也許你要問爲什麼編譯出的靜態庫是Utils.lib,編譯出的動態庫也有Utils.lib,這兩個.lib文件是一樣的嗎? 
你比較一下兩個.lib文件的大小就會發現相差很大(靜態庫的lib有235KB,動態庫的lib只有2.7KB),所以肯定不是一樣的啦!動態庫對應的lib文件叫“導入庫”,導入庫只包含了地址符號表等,確保調用方的程序能找到對應函數的一些基本地址信息,而實際的執行代碼位於DLL文件中。靜態庫的lib文件本身就包含了實際執行代碼、符號表等。

使用導入(第三方)庫

在實際的開發中經常要用第三方提供的庫,如開源庫,或大型系統中合作方提供的組件。如果使用呢?我們就以上面自己製作的庫爲例進行講解。假設我們有一個工程TestProject要使用上面自己製作的Utils庫。

使用靜態庫

  1. 右鍵工程->Properties,進行如下的設置。 

    設置頭文件所在的路徑
    設置頭文件所在的路徑

    設置lib庫所在的路徑
    設置lib庫所在的路徑

    設置要導入哪個lib庫
    設置要導入哪個lib庫

  2. 測試代碼如下:

#include <iostream>
#include <tchar.h>
#include "Utils.h"

int _tmain(int argc, _TCHAR* argv[])
{
    int nMax = Utils::Max(25, 37);
    std::cout << nMax << std::endl;
    int nMin = Utils::Min(10, 44);
    std::cout << nMin << std::endl;
    int nValue = Utils::Range(0, 100, 115);
    std::cout << nValue << std::endl;
    char* pStr = "1234";
    int nValue2 = ConvertToInt(pStr);
    std::cout << nValue2 << std::endl;
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

使用動態庫

  1. 右鍵TestProject工程->Properties,進行如下的設置。 

    設置頭文件所在的路徑
    設置頭文件所在的路徑 
    設置lib庫所在的路徑
    設置lib庫所在的路徑 
    設置要導入哪個導入庫
    設置要導入哪個導入庫

  2. 將Utils.dll放入與TestProject的輸出文件TestProject.exe相同的路徑下。這個很最重,不然會編譯成功會是執行失敗,因爲找不到對應的.dll文件。

  3. 測試代碼與靜態庫的一樣。



附錄

Utils.cpp

#include "Utils.h"

Utils::Utils(void)
{
}

Utils::~Utils(void)
{
}


int Utils::Max( int nValue1, int nValue2 )
{
    return nValue1 > nValue2 ? nValue1 : nValue2;
}

int Utils::Min( int nValue1, int nValue2 )
{
    return nValue1 < nValue2 ? nValue1 : nValue2;
}

int Utils::Range( int nMin, int nMax, int nValue )
{
    if (nMax < nMin)
    {
        int temp = nMin;
        nMin = nMax;
        nMax = temp;
    }

    if (nValue < nMin)
    {
        return nMin;
    } else if (nValue > nMax)
    {
        return nMax;
    } else
    {
        return nValue;
    }
}

int ConvertToInt( const char* pStr )
{
    int val;  
    std::strstream ss;  
    ss << pStr;  
    ss >> val;  
    return val;  
}
  • 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
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 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
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

參考文章:C++靜態庫與動態庫

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章