C++高手進階:Windows 模塊加載的藝術與策略

前文我們講到了怎么不依賴第三庫,搭建自己的測試框架
沒有看的讀者可以通過這個鏈接自行閱讀:
👉👉👉 自力更生:0依賴三方庫,手把手教你打造專屬C++測試框架
作為項目開發來說,我們通常會將不同的功能、數據進行分層或分塊,這其實就涉及到不同模塊之間的相互調用。選擇什么樣的策略來實現模塊間能力的交互,其實是非常重要的。
本文中,我們就來抽象業務邏輯,看下模塊間的加載是如何實現的。

文章目錄

    • 模塊間的加載方式
      • 動態加載(Dynamic Loading)
      • 靜態加載(Static Loading)
    • 什么時候用什么方式
    • 怎么用
      • 動態加載示例介紹
      • 靜態加載示例介紹
    • 總結

模塊間的加載方式

通常而言,模塊間的加載方式可以分為兩類:

  • 動態加載
  • 靜態加載

什么是動態加載呢?簡單而言就是模塊間遵循動態加載協議,平臺層模塊或業務層模塊主動的調用 LoadLibrary()GetProcAddress接口來加載其他模塊。

什么是靜態加載呢?簡單而言就是模塊間遵循靜態加載協議,平臺層模塊或業務層模塊在編譯時就將其他模塊的依賴關系鏈接進來(通常建議在編寫平臺層 CMakeLists.txt 時,將依賴的庫或模塊列入鏈接庫的列表,并設置鏈接類型為靜態庫)。

筆者在撰寫此文時特地查了相關資料,對它們的特點做下介紹(PS: 文中有些地方可能有誤,如有疑問,歡迎指正):

動態加載(Dynamic Loading)

動態加載指的是在程序運行時,根據需要加載和卸載庫或模塊。這種方式有以下幾個特點:

  1. 運行時鏈接:庫的代碼在程序運行時才被加載,通常是通過動態鏈接庫(Dynamic Link Library,DLL)在Windows上或共享庫(Shared Object,SO)在Unix-like系統上實現。
  2. 靈活性:可以在不重啟程序的情況下加載或卸載模塊,提供更高的靈活性。
  3. 內存使用:只需要加載正在使用的庫,節省內存。
  4. 更新和維護:可以獨立更新庫而不需要重新編譯整個程序。
  5. 依賴管理:需要更復雜的依賴管理,因為庫在運行時才被加載。

靜態加載(Static Loading)

靜態加載指的是在程序編譯時,庫或模塊已經被鏈接到程序中。這種方式有以下幾個特點:

  1. 編譯時鏈接:庫的代碼在編譯期間被鏈接到最終的可執行文件中。
  2. 加載時間:因為庫的代碼已經是程序的一部分,所以不需要在程序運行時加載。
  3. 內存使用:靜態加載的庫會占用更多的內存,因為所有庫的代碼都會被包含在最終的可執行文件中。
  4. 更新和維護:更新靜態加載的庫可能需要重新編譯整個程序。
  5. 依賴管理:靜態加載簡化了依賴管理,因為所有依賴都包含在程序中。

什么時候用什么方式

也許讀者在其他地方看到過它的使用建議,比如說根據加載的時間內存的使用模塊間交互的頻繁性模塊間的依賴關系復雜度模塊的更新頻率等方面進行選擇。

筆者就不在從這些維度進行闡述了。

筆者從工程實踐的角度出發,講講兩種使用的場景:

  1. 當主模塊沒有代碼邏輯依賴該模塊暴露的API接口時,該模塊的能力僅僅是主模塊能力的增強,就可以采用動態加載方式。
  2. 當主模塊有代碼邏輯依賴該模塊暴露的API接口時,該模塊的能力是主模塊的核心功能,就可以采用靜態加載方式。

怎么用

動態加載示例介紹

場景: 假設有主模塊A,在其上開發了模塊B,以進行能力增強。

那么對于模塊 A 來說,得做如下事情:

  1. 定義模塊間交互的協議類 TestModuleBase,用于模塊 B 來派生,自定義自己的加載、卸載業務邏輯。
// xx.h
class TestModuleBase
{
public:TestModuleBase();virtual ~TestModuleBase();virtual SystemStatus initialize();virtual SystemStatus uninitialize();private:TestModuleBase(const TestModuleBase&) = delete;TestModuleBase& operator=(const TestModuleBase&) = delete;
};typedef TestModuleBase* (__cdecl* RxProlModule)();// xx.cpp
TestModuleBase::TestModuleBase()
{}TestModuleBase::~TestModuleBase()
{}SystemStatus TestModuleBase::initialize()
{return SystemStatus::e_Ok;
}SystemStatus TestModuleBase::uninitialize()
{return SystemStatus::e_Ok;
}
  1. 定義一個模塊加載器 TestModuleLoader 類,用來管理模塊的加載和卸載。
// xx.h
class DEMO_TEST_STATIC_EXPORT TestModuleLoader
{
public:TestModuleLoader() = delete;static SystemStatus loadModule(const wchar_t* pPath);static SystemStatus unloadModule(const wchar_t* pPath);private:TestModuleLoader(const TestModuleLoader&) = delete;TestModuleLoader& operator=(const TestModuleLoader&) = delete;
};// xx.cpp
SystemStatus TestModuleLoader::loadModule(const wchar_t* pPath)
{HMODULE pHandleModule = LoadLibrary(pPath);if (!pHandleModule == NULL) {return SystemStatus::e_Fail;}RxProlModule rxProlModule = (RxProlModule)(GetProcAddress(pHandleModule, "rxProtocalModule"));if (rxProlModule == NULL) {FreeLibrary(pHandleModule);return SystemStatus::e_Fail;}TestModuleBase* pModule = rxProlModule();if (pModule == NULL) {FreeLibrary(pHandleModule);return SystemStatus::e_Fail;}return pModule->initialize();
}SystemStatus TestModuleLoader::unloadModule(const wchar_t* pPath)
{if (pPath == NULL) {return SystemStatus::e_Fail;}HMODULE pHandleModule = GetModuleHandle(pPath);if (pHandleModule == NULL) {return SystemStatus::e_Fail;}RxProlModule rxProlModule = (RxProlModule)(GetProcAddress(pHandleModule, "rxProtocalModule"));if (rxProlModule == NULL) {FreeLibrary(pHandleModule);return SystemStatus::e_Fail;}TestModuleBase* pModule = rxProlModule();if (pModule == NULL) {FreeLibrary(pHandleModule);return SystemStatus::e_Fail;}SystemStatus uninitResult = pModule->uninitialize();if (uninitResult != SystemStatus::e_Ok) {return uninitResult;}FreeLibrary(pHandleModule);return SystemStatus::e_Ok;
}
  1. 在適當的時機,比如說模塊A初始化和反初始化的時候,對模塊B進行主動的加載和卸載。
SystemStatus PlatformModule::initialize()
{...auto ss = TestModuleLoader::loadModule("DemoModule1.dll")if (ss != SystenStatus::e_Ok) {return ss;}...
}SystemStatus PlatformModule::uninitialize()
{...auto ss = TestModuleLoader::unloadModule("DemoModule1.dll")if (ss != SystenStatus::e_Ok) {return ss;}...
}

那么對于模塊 B 來說,得做如下事情:

  1. 派生協議類 TestModuleBase,實現自己的加載、卸載業務邏輯。
// xx.h
class DemoTestModule :public TestModuleBase
{
public:DemoTestModule();~DemoTestModule();SystemStatus initialize() override;SystemStatus uninitialize() override;private:DemoTestModule(const DemoTestModule&) = delete;DemoTestModule& operator=(const DemoTestModule&) = delete;bool m_initialized = false;
};DEMO_TEST_C_EXPORT TestModuleBase* rxProtocalModule();// xx.cpp
DEMO_TEST_C_EXPORT TestModuleBase* rxProtocalModule()
{static DemoTestModule module;return &module;
}DemoTestModule::DemoTestModule()
{}DemoTestModule::~DemoTestModule()
{}SystemStatus DemoTestModule::initialize()
{if (m_initialized) {return SystemStatus::e_NotSpecified;}m_initialized = true;// do something for initreturn SystemStatus::e_Ok;
}SystemStatus DemoTestModule::uninitialize()
{if (m_initialized) {// do uninit thingm_initialized = false;}return SystemStatus::e_Ok;
}

靜態加載示例介紹

場景: 假設有主模塊A,在其上開發了模塊B,以實現核心功能。

  1. 對于模塊B 來說,需定義一個模塊初始化的類,實現該模塊中對象的初始化和反初始化。同時給該類設置一個靜態對象。
class DemoModuleInit
{
public:DemoModuleInit(){// do init thing}~DemoModuleInit(){// do uninit thing}
};static DemoModuleInit g_demoModuleInit;
  1. 在模塊 A 的CMakeLists.txt中,將模塊B的靜態庫或模塊鏈接進來。
link_directories("${PROJECT_SOURCE_DIR}/lib")
...
link_libraries(DemoModule1)

總結

以上就是模塊間加載的兩種方式,動態加載和靜態加載,以及它們的使用場景和示例。希望能對讀者有所幫助。

如果讀者有不理解的地方,歡迎私信交流。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/19816.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/19816.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/19816.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Leetcode:最長回文子串

題目鏈接:5. 最長回文子串 - 力扣(LeetCode) 普通版本(暴力枚舉) 解題關鍵: 1、記錄最長回文字串的長度和起始字符的下標 2、判斷回文字串的邏輯與整體邏輯分離 3、先確定尋找回文字串的邊界范圍后從兩邊向…

解析Java中1000個常用類:CharSequence類,你學會了嗎?

在 Java 編程中,字符串操作是最常見的任務之一。為了提供一種靈活且統一的方式來處理不同類型的字符序列,Java 引入了 CharSequence 接口。 通過實現 CharSequence 接口,各種字符序列類可以提供一致的 API,增強了代碼的靈活性和可擴展性。 本文將深入探討 CharSequence 接…

NBM 算法【python,算法,機器學習】

樸素貝葉斯法(Naive Bayes model)是基于貝葉斯定理與特征條件獨立假設的分類方法。 貝葉斯定理 P ( A ∣ B ) P ( B ∣ A ) ? P ( A ) P ( B ) P(A|B)\frac{P(B|A) * P(A)}{P(B)} P(A∣B)P(B)P(B∣A)?P(A)? 其中A表示分類,B表示屬性&…

Unity中的MVC框架

基本概念 MVC全名是Model View Controller 是模型(model)-視圖(view)-控制器(controller)的縮寫 是一種軟件設計規范,用一種業務邏輯、數據、界面顯示 分離的方法組織代碼 將業務邏輯聚集到一個部件里面,在改進和個性化定制界面及用戶交互的同時&#x…

【嵌入式硬件】DRV8874電機驅動

目錄 1 芯片介紹 1.1 特性簡介 1.2 引腳配置 1.3 最佳運行條件 2 詳細說明 2.1 PMODE配置控制模式 2.1.1 PH/EN 控制模式 2.1.2 PWM 控制模式 2.1.3 獨立半橋控制模式 2.2 電流感測和調節 2.2.1 IPROPI電流感測 2.2.2 IMODE電流調節 3.應用 3.1設計要求 3.2 設計…

AI換臉FaceFusion一鍵云部署指南

大家好,從我開始分享到現在,收到很多朋友的反饋說配置很低玩不了AI。本篇是一個云端部署AI項目的指南,幫助大家在云端進行AI項目的部署。我會從云平臺的選擇、代碼部署、保存鏡像幾個方面進行詳細的介紹。沒有代碼基礎的小白也不用擔心&#…

exe4j innosetup

exe4j:jdk: 打包時:需要的文件最好放到單獨的一個文件夾下,主機安裝32位jdk,exe4j用32位的。 附帶jre: jre用32位的(jdk下的jre)可使用X86,X64.用相對路徑。 只打64位時,需要選擇32-bit or 64-bit (generate 64…

樂觀鎖 or 悲觀鎖 你怎么選?

你有沒有聽過這樣一句話:悲觀者正確,樂觀者成功?。那么今天我來分享下什么是樂觀鎖?和悲觀鎖。 樂觀鎖和悲觀鎖有什么區別,它們什么場景會用 樂觀鎖 樂觀鎖基于這樣的假設:多個事務在同一時間對同一數據對象進行操作的可能性很…

fps游戲中如何將矩陣轉換為二維屏幕上的矩形坐標

fps游戲中如何將矩陣轉換為二維屏幕上的矩形坐標 matrix[4][4]: 4x4 矩陣,通常用于3D變換(如模型視圖投影矩陣)。 float* location: 一個指向位置坐標的指針,表示要轉換的3D位置。 int Window_w, int Window_h: 窗口的寬度和高…

工廠模式詳情

一.介紹工廠模式的用途與特點 工廠方法模式是一種創建型設計模式, 其在父類中提供一個創建對象的方法, 允許子類決定實例化對象的類型。定義工廠方法模式(Fatory Method Pattern)是指定義一個創建對象的接口,但讓實現這個接口的類來決定實例…

Python導出Jira列表

import requests import urllib3 urllib3.disable_warnings() from jira import JIRA import pandas as pd def login_jira(username,password):jira JIRA("https://jira.cn/",basic_auth(username,password))#projectsjira.project(id13)# jqlproject"云鏈-…

基于強化學習的控制率參數自主尋優

1.介紹 針對控制建模與設計場景中控制參數難以確定的普遍問題,提出了一種基于強化學習的控制律參數自主優化解決方案。該方案以客戶設計的控制律模型為基礎,根據自定義的控制性能指標,自主搜索并確定最優的、可狀態依賴的控制參數組合。 可…

unity打包的WebGL部署到IIS問題

部署之后會出錯,我遇到的有以下幾種; 進度條卡住不動 明明已經部署到了IIS上,為什么瀏覽網頁的時候還是過不去或者直接報錯。 進度條卡住不動的問題其實就是wasm和data的錯誤。 此時在瀏覽器上按F12進入開發者模式查看錯誤(下圖…

前端知識點雜記

本文章用于記錄前端學習中遇到的瑣碎問題及解決方法,歡迎大家一起學習補充~ 前端如何獲取UUID發送至后端? 1. 命令行下載uuid庫 npm install uuid 2. 工程導入uuid庫 import { v4 as uuidv4 } from "uuid"; 3. 使用方法生成uuid實例 co…

付費工具邏輯

1.付費推廣目的: 傳播信息心理暗示;擴大銷售,指導消費;樹立形象,塑道品牌; 2.付費和免費廣告: 付費主要為了增加曝光;免費廣告一般比付費廣告轉化率高; 3.平臺廣告交…

《Kubernetes部署篇:基于麒麟V10+ARM64架構部署harbor v2.4.0鏡像倉庫》

總結:整理不易,如果對你有幫助,可否點贊關注一下? 更多詳細內容請參考:企業級K8s集群運維實戰 一、環境信息 K8S版本 操作系統 CPU架構 服務版本 1.26.15 Kylin Linux Advanced Server V10 ARM64 harbor v2.4.0 二、部…

chrome谷歌瀏覽器開啟Gemini Nano模型

前提 確保您的操作系統語言設置為英語(美國) 可能還需要將 Chrome 瀏覽器的語言更改為英語(美國)。 下載dev或Canary版本Chrome Chrome Canary Chrome Dev 注意:確認您的版本高于 127.0.6512.0。 其中一個Chrome版本不行就切換另外一個版本 繞過性能檢查 Tab輸入: …

中國美業元宇宙-探索美容行業的未來

隨著科技的不斷進步和數字化轉型的深入,元宇宙作為一種全新的虛擬現實交互平臺,正逐漸成為推動多個行業革新的重要力量。在這種背景下,中國美業也在積極擁抱元宇宙,希望通過這一新興技術為傳統美容行業帶來創新與發展。 #### 中國…

結構體相關習題的補充

結構體相關習題的補充 題目1: 如有以下代碼: struct student {int num;char name[32];float score; }stu;則下面的敘述不正確的是:( ) A.struct 是結構體類型的關鍵字 B.struct student 是用戶定義的結構體類型 C.num, score 都是結構體…

正邦科技(day4)

燒錄 一、燒錄固件二、 通訊模塊升級1:USB的方式升級固件2:通過mqtt的方式升級固件3:切換環境 三、 燒錄WiFi1:短接2:燒錄腳本 設備注意事項: 第一種方式:通信模組和MCU都可以統一燒錄BoodLoade…