在絕大多數時候,咱們都不須要從頭從新造輪子,直接使用別人封裝好的庫會讓開發變得更高效、更易於管理複雜項目。本文將簡單介紹靜態連接庫的基本技術。ios
很好,若是有人跟你說這種話,那麼請讓他在無論用什麼編程語言的時候都本身實現標準輸入、標準輸出功能;並且,必定要讓他本身寫出本身用的操做系統,讓他本身寫出BIOS芯片組的固件程序……這種人,懟之,使其滾之,敬而遠之。程序員
在一個特定的項目裏,能夠用形形色色的頭文件來管理類與函數的聲明,可是侷限性也很容易被看出來。首先,爲了更有效的在以後的項目中使用頭文件,那麼天然會針對每一種類或函數去書寫一份頭文件,這就會致使頭文件的數量變得不少;並且頭文件是不參與編譯的,爲也能讓項目編譯經過,還要再把對應的實現源文件也引入到工程中。最重要的一點是保密性。頭文件和源文件若是要給別人使用,那麼別人都是能夠直接修改源碼的,儘管我本人是開源的支持者,但咱們並不能保證每一個人都是會遵照開源協議的。編程
這點確實沒錯,可是用庫的最大優點在於,庫是編譯過的,庫只參與生成應用程序的連接過程——這也就是「連接庫」這個名字的來歷。數組
以上總結了常見的對庫持懷疑態度的人的見解,下面說一下爲何仍是要推崇庫。編程語言
使用庫,程序開發者能夠不用去操心這個庫是怎麼實現的——並非說程序開發者必定不會去實現那些他要用到的功能,只是在工程開發中,沒有必要再去實現,由於寫重複代碼是最低效的開發方式——即便那些重複的部分具備至關高的技術含量。
藉助已有的庫,咱們能夠把工做重心轉移到更高級的功能開發上去,而不是成天抱着輪子去嘲諷跑車。函數
既然咱們會想到把頗有技術含量並且還又是不少人都會用到的功能寫成庫以便再次使用,那麼有商業頭腦的人天然也能想獲得。庫是編譯器編譯事後的產物,對於C++來講,編譯是不可逆的——就算是有逆向工程,那麼不可能百分百還原,尚且逆向還都是靠有經驗的程序員去猜編譯前可能會是什麼樣子的。使用庫咱們就能夠很好的隱藏本身的實現細節,而只把預留的接口給用戶(指使用庫的程序員)。
若是哪天咱們本身寫出了一套至關具備商業價值的庫,咱們一樣可使用庫的方式來發布、售賣咱們的技術。工具
由於庫的編譯過的,因此使用庫而不是直接引入源代碼在編譯大型項目時會大大提升編譯效率。測試
靜態庫是編譯產生的二進制文件,在使用靜態庫的程序的最終編譯過程當中,連接器從庫中複製這些函數與數據並把它們和應用程序的其餘模塊組合起來,生成可執行文件。庫是不能夠被執行的——或者說,庫沒有執行這個概念。
本文以Windows平臺爲例講解,雖然中間步驟與Linux相差很大,可是學會了靜態庫的基礎知識後,在其餘平臺的使用其實也換湯不換藥。
由於靜態庫是編譯事後的產物,因此用戶是無法知道庫中的實現信息的,但這也致使了一個問題:庫中的函數和類用戶也是不知道如何調用。爲了繞開這個問題,咱們能夠利用C++中把聲明與實現分離的技巧,給庫配備一個面向用戶的用於聲明接口的頭文件。下面咱們先建立一個靜態庫:spa
而後咱們看一下這個靜態庫項目的屬性:操作系統
能夠看到,編譯後生成的將是一個.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++中的一部分特性寫進靜態庫,關於更多的技巧,請自行查閱更多資料。
固然,靜態庫也有不少缺點,並且,當庫中的代碼量很大但咱們卻不須要所有用到的時候,或者是咱們想更靈活地使用庫,那麼這個時候咱們就要用到使用更爲複雜也支持更多特性的另外一種庫——動態連接庫。關於動態連接庫,下一篇文章將會介紹。