一、動態庫
通過之前靜態庫那篇文章的介紹,發現靜態庫更容易使用和理解,也達到了代碼復用的目的,那為什么還需要動態庫呢?
1、為什么還需要動態庫?
為什么需要動態庫,其實也是靜態庫的特點導致,
? 空間浪費是靜態庫的一個問題,

? 另一個問題是靜態庫對程式的更新、部署和發布頁會帶來麻煩,如果靜態庫liba.lib更新了,所以使用它的應用程式都需要重新編譯、發布給用戶(對于玩家來說,可能是一個很小的改動,卻導致整個程式重新下載,全量更新),
動態庫在程式編譯時并不會被連接到目標代碼中,而是在程式運行是才被載入,不同的應用程式如果呼叫相同的庫,那么在記憶體里只需要有一份該共享庫的實體,規避了空間浪費問題,動態庫在程式運行是才被載入,也解決了靜態庫對程式的更新、部署和發布頁會帶來麻煩,用戶只需要更新動態庫即可,增量更新,
動態庫特點總結:
? 動態庫把對一些庫函式的鏈接載入推遲到程式運行的時期,
? 可以實作行程之間的資源共享,(因此動態庫也稱為共享庫)
? 將一些程式升級變得簡單,
? 甚至可以真正做到鏈接載入完全由程式員在程式代碼中控制(顯示呼叫),
Window與Linux執行檔案格式不同,在創建動態庫的時候有一些差異,
? 在Windows系統下的執行檔案格式是PE格式,動態庫需要一個DllMain函式做出初始化的入口,通常在匯出函式的宣告時需要有_declspec(dllexport)關鍵字,
? Linux下gcc編譯的執行檔案默認是ELF格式,不需要初始化入口,亦不需要函式做特別的宣告,撰寫比較方便,
與創建靜態庫不同的是,不需要打包工具(ar、lib.exe),直接使用編譯器即可創建動態庫,
2、Linux下創建與使用動態庫
linux動態庫的命名規則為:
元件的名字形式為 libxxx.so,前綴是lib,后綴名為“.so”,
? 針對于實際庫檔案,每個共享庫都有個特殊的名字“soname”,在程式啟動后,程式通過這個名字來告訴動態加載器該載入哪個共享庫,
? 在檔案系統中,soname僅是一個鏈接到實際動態庫的鏈接,對于動態庫而言,每個庫實際上都有另一個名字給編譯器來用,它是一個指向實際庫鏡像檔案的鏈接檔案(lib+soname+.so),
3、創建動態庫(.so)
撰寫四則運算動態庫代碼:
#pragma once class DynamicMath { public: DynamicMath(void); ~DynamicMath(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(); };
首先,生成目標檔案,此時要加編譯器選項-fpic
g++ -fPIC -c DynamicMath.cpp
-fPIC 創建與地址無關的編譯程式(pic,position independent code),是為了能夠在多個應用程式間共享,
然后,生成動態庫,此時要加聯結器選項-shared
g++ -shared -o libdynmath.so DynamicMath.o
-shared指定生成元件,

其實上面兩個步驟可以合并為一個命令:
g++ -fPIC -shared -o libdynmath.so DynamicMath.cpp
4、使用動態庫
撰寫使用動態庫的測驗代碼:
#include "../DynamicLibrary/DynamicMath.h" #include <iostream> using namespace std; int main(int argc, char* 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; DynamicMath dyn; dyn.print(); return 0; }
參考動態庫編譯成可執行檔案(跟靜態庫方式一樣):
g++ TestDynamicLibrary.cpp -L../DynamicLibrary -ldynmath
然后運行:./a.out,發現竟然報錯了!!!

可能大家會猜測,是因為動態庫跟測驗程式不是一個目錄,那我們驗證下是否如此:

發現還是報錯!!!那么,在執行的時候是如何定位共享庫檔案的呢?
1) 當系統加載可執行代碼時候,能夠知道其所依賴的庫的名字,但是還需要知道絕對路徑,此時就需要系統動態載入器(dynamic linker/loader),
2) 對于elf格式的可執行程式,是由ld-linux.so*來完成的,它先后搜索elf檔案的 DT_RPATH段—環境變數LD_LIBRARY_PATH—/etc/ld.so.cache檔案串列—/lib/,/usr/lib 目錄找到庫檔案后將其載入記憶體,
——————————
如何讓系統能夠找到它:
? 如果安裝在/lib或者/usr/lib下,那么ld默認能夠找到,無需其他操作,
? 如果安裝在其他目錄,需要將其添加到/etc/ld.so.cache檔案中,步驟如下:
1. 編輯/etc/ld.so.conf檔案,加入庫檔案所在目錄的路徑
2. 運行ldconfig ,該命令會重建/etc/ld.so.cache檔案
我們將創建的動態庫復制到/usr/lib下面,然后運行測驗程式,

二、Windows下創建與使用動態庫
1、創建動態庫(.dll)
與Linux相比,在Windows系統下創建動態庫要稍微麻煩一些,首先,需要一個DllMain函式做出初始化的入口(創建win32控制臺程式時,勾選DLL型別會自動生成這個檔案):
// dllmain.cpp : Defines the entry point for the DLL application. #include "stdafx.h" BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
通常在匯出函式的宣告時需要有_declspec(dllexport)關鍵字:
#pragma once class DynamicMath { public: __declspec(dllexport) DynamicMath(void); __declspec(dllexport) ~DynamicMath(void); static __declspec(dllexport) double add(double a, double b);//加法 static __declspec(dllexport) double sub(double a, double b);//減法 static __declspec(dllexport) double mul(double a, double b);//乘法 static __declspec(dllexport) double div(double a, double b);//除法 __declspec(dllexport) void print(); };
生成動態庫需要設定工程屬性,打開工程“屬性面板”→”配置屬性”→”常規”,配置型別選擇動態庫,

Build專案即可生成動態庫,
2、使用動態庫
創建win32控制臺測驗程式:
#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; DynamicMath dyn; dyn.print(); system("pause"); return 0; }
? 方法一:
工程“屬性面板”→“通用屬性”→“框架和參考”→”添加參考”,將顯示“添加參考”對話框,
“專案”選項卡列出了當前解決方案中的各個專案以及可以參考的所有庫, 在“專案”選項卡中,選擇 DynamicLibrary, 單擊“確定”,

添加DynamicMath.h 頭檔案目錄,必須修改包含目錄路徑,打開工程“屬性面板”→”配置屬性”→“C/C++”→” 常規”,在“附加包含目錄”屬性值中,鍵入DynamicMath.h 頭檔案所在目錄的路徑或瀏覽至該目錄,

編譯運行OK,

? 方法二:
“屬性面板”→”配置屬性”→“聯結器”→”常規”,附加依賴庫目錄中輸入,動態庫所在目錄;

“屬性面板”→”配置屬性”→“聯結器”→”輸入”,附加依賴庫中輸入動態庫編譯出來的DynamicLibrary.lib,

靜態庫與動態庫深入研究——總結
結合之前的靜態庫與本篇動態庫,總結出來二者的不同點在于代碼被載入的時刻不同,
? 靜態庫在程式編譯時會被連接到目標代碼中,程式運行時將不再需要該靜態庫,因此體積較大,
? 動態庫在程式編譯時并不會被連接到目標代碼中,而是在程式運行是才被載入,因此在程式運行時還需要動態庫存在,因此代碼體積較小,
動態庫的好處是,不同的應用程式如果呼叫相同的庫,那么在記憶體里只需要有一份該共享庫的實體,帶來好處的同時,也會有問題!如經典的DLL Hell問題,關于如何規避動態庫管理問題,可以自行查找相關資料,

最后,不管你是轉行也好,初學也罷,進階也可,如果你想學編程~
【值得關注】我的 C/C++編程學習交流俱樂部【點擊進入】
問題答疑,學習交流,技術探討,還有超多編程資源大全,零基礎的視頻也超棒~
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/262342.html
標籤:C++
