原生C++實現信號與槽機制:原理詳解

信號與槽機制是一種廣泛應用于事件驅動系統和GUI框架(如Qt)的設計模式。它允許組件之間通過訂閱-發布模式進行通信,從而實現松耦合的設計。本文將詳細講解如何在原生C++中從零開始實現信號與槽機制,并深入探討其工作原理。


一、信號與槽機制的核心概念

信號與槽機制的核心思想是:一個組件(信號)可以發出某種事件,其他組件(槽)可以訂閱該事件,并在事件觸發時執行相應的操作。這種設計模式的優勢在于:

  • 松耦合:信號和槽之間沒有直接的依賴關系,提高了系統的靈活性和可維護性。
  • 可擴展性:可以輕松地添加新的槽函數,而無需修改信號的代碼。
  • 事件驅動:適用于需要異步處理事件的場景。

二、實現信號與槽機制的步驟

1. 定義Signal類

首先,我們需要定義一個Signal類,用于管理槽函數的連接和觸發。

#include <vector>
#include <functional>
#include <mutex>template<typename... Args>
class Signal {
public:// 連接槽函數void connect(std::function<void(Args...)> slot) {std::lock_guard<std::mutex> lock(mutex_);slots.push_back(slot);}// 斷開槽函數void disconnect(std::function<void(Args...)> slot) {std::lock_guard<std::mutex> lock(mutex_);auto it = std::remove(slots.begin(), slots.end(), slot);slots.erase(it, slots.end());}// 觸發信號void emit(Args... args) {std::lock_guard<std::mutex> lock(mutex_);for (const auto& slot : slots) {slot(args...);}}private:std::vector<std::function<void(Args...)>> slots;std::mutex mutex_;
};

關鍵點解釋

  • 模板參數Args :使用模板參數Args,使得Signal類可以支持不同類型的信號和槽函數。Args...表示可變參數模板,支持任意數量和類型的參數。
  • connect方法:用于將槽函數連接到信號上。槽函數被存儲在一個std::vector<std::function<void(Args...)>>容器中。
  • disconnect方法:用于斷開已連接的槽函數。通過std::removeerase操作,從容器中移除指定的槽函數。
  • emit方法:用于觸發信號,并將參數傳遞給所有連接的槽函數。遍歷容器中的所有槽函數,并依次調用它們。
  • 線程安全性:使用std::mutexstd::lock_guard,確保在多線程環境下對槽函數容器的操作是線程安全的。

2. 定義槽函數

槽函數是響應信號觸發的操作。以下是兩個簡單的槽函數示例:

#include <iostream>void slot1() {std::cout << "Slot 1 triggered!" << std::endl;
}void slot2(int arg) {std::cout << "Slot 2 received argument: " << arg << std::endl;
}

說明

  • 槽函數可以是任何接受特定參數并執行相應操作的函數或lambda表達式。
  • 槽函數的類型必須與信號的模板參數匹配。

3. 使用Signal類

main函數中,我們可以創建一個Signal對象,并連接槽函數。

int main() {// 創建一個Signal對象,用于發送通知Signal<std::string> notificationSignal;// 連接槽函數notificationSignal.connect(onNotificationReceived);// 發送通知notificationSignal.emit("Hello, YongYong! This is a notification from Guoyao.");return 0;
}

說明

  • Signal<std::string>表示該信號接受一個字符串參數。
  • connect方法將槽函數onNotificationReceived連接到信號上。
  • emit方法觸發信號,并將通知消息傳遞給所有連接的槽函數。

三、實現原理的深入分析

1. 模板與參數化

Signal類使用模板參數Args,使得它可以支持不同類型的信號和槽函數。這種參數化設計使得信號與槽機制具有高度的靈活性和可擴展性。

template<typename... Args>
class Signal {// ...
};

通過模板參數,Signal類可以處理任意數量和類型的參數。例如:

  • Signal<void>:不帶參數的信號。
  • Signal<int>:帶一個整數參數的信號。
  • Signal<int, double>:帶兩個參數(整數和浮點數)的信號。

2. 槽函數的存儲與管理

槽函數被存儲在一個std::vector<std::function<void(Args...)>>容器中。std::function是一個通用的函數包裝器,可以存儲任何可調用對象(如函數指針、lambda表達式等)。

std::vector<std::function<void(Args...)>> slots;

槽函數的連接與斷開

  • connect方法將槽函數添加到容器中。
  • disconnect方法從容器中移除指定的槽函數。

注意事項

  • 由于std::function的比較操作可能不準確,斷開槽函數時需要確保傳遞的槽函數與連接時完全相同。
  • 如果使用lambda表達式作為槽函數,可能會遇到比較操作的問題,因為lambda表達式是匿名的,無法直接比較。

3. 信號的觸發

emit方法負責觸發信號,并將參數傳遞給所有連接的槽函數。

void emit(Args... args) {std::lock_guard<std::mutex> lock(mutex_);for (const auto& slot : slots) {slot(args...);}
}

關鍵點

  • 使用std::lock_guard確保在多線程環境下對槽函數容器的訪問是線程安全的。
  • 遍歷容器中的所有槽函數,并依次調用它們,傳遞參數。

4. 線程安全性

在多線程環境下,信號與槽機制需要確保對槽函數容器的操作是線程安全的。Signal類通過使用std::mutexstd::lock_guard實現了這一點。

std::mutex mutex_;

線程安全操作

  • connectdisconnectemit方法中,使用std::lock_guardmutex_進行加鎖,確保同一時間只有一個線程可以操作槽函數容器。

四、實際應用示例

場景描述

假設我們有一個“國遙”模塊需要向“勇勇(YongYong)”發送通知。我們可以使用信號與槽機制來實現這一功能。

實現代碼

以下是完整的代碼示例:

#include <vector>
#include <functional>
#include <mutex>
#include <iostream>
#include <string>template<typename... Args>
class Signal {
public:// 連接槽函數void connect(std::function<void(Args...)> slot) {std::lock_guard<std::mutex> lock(mutex_);slots.push_back(slot);}// 斷開槽函數void disconnect(std::function<void(Args...)> slot) {std::lock_guard<std::mutex> lock(mutex_);auto it = std::remove(slots.begin(), slots.end(), slot);slots.erase(it, slots.end());}// 觸發信號void emit(Args... args) {std::lock_guard<std::mutex> lock(mutex_);for (const auto& slot : slots) {slot(args...);}}private:std::vector<std::function<void(Args...)>> slots;std::mutex mutex_;
};// 定義槽函數
void onNotificationReceived(const std::string& message) {std::cout << "Notification received by YongYong: " << message << std::endl;
}int main() {// 創建一個Signal對象,用于發送通知Signal<std::string> notificationSignal;// 連接YongYong的槽函數notificationSignal.connect(onNotificationReceived);// 發送通知notificationSignal.emit("Hello, YongYong! This is a notification from Guoyao.");return 0;
}

運行結果

運行上述代碼后,控制臺將輸出:

Notification received by YongYong: Hello, YongYong! This is a notification from Guoyao.

這表明“國遙”成功地向“勇勇(YongYong)”發送了通知,且通知被正確接收和處理。


五、總結與擴展

通過以上步驟,我們成功地實現了“國遙向勇勇(YongYong)發送通知”的信號與槽機制。這一機制不僅提高了代碼的模塊化和可維護性,還使得系統中的不同模塊之間能夠高效、靈活地進行通信。

在實際項目中,可以根據具體需求進一步擴展和優化這一機制,例如:

  • 支持弱引用:防止槽函數持有對象而導致的內存泄漏。可以通過std::weak_ptr來實現。
  • 信號繼承與重載:允許信號被繼承和重載,以支持更復雜的事件處理邏輯。
  • 信號過濾:在觸發信號時,根據某些條件過濾槽函數,避免不必要的調用。

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

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

相關文章

【人工智能】OpenAI的AI代理革命:通向超擬人交互的未來之路

人工智能代理(AI Agent)正引領一場深刻的技術變革,其核心在于賦予AI系統感知、規劃、行動和學習的能力,以自主完成復雜任務。OpenAI作為這一領域的先驅,通過其大型語言模型(LLMs)如GPT系列,極大地推動了AI代理的發展,使其在自然語言理解、生成和工具使用方面達到了前所…

Ubuntu虛擬機介紹、命令、安裝軟件指令(一)

Ubuntu介紹 Ubuntu 是一個基于 Debian 的開源 Linux 操作系統&#xff0c;由 Canonical 公司贊助開發。它是目前全球最流行的 Linux 發行版之一&#xff0c;以其用戶友好性、穩定性和強大的社區支持著稱。 核心特性 1.免費開源 完全免費使用和分發&#xff0c;遵循 GNU GPL…

企業微信服務商創建第三方應用配置數據回調url和指令回調url的java代碼實現

關鍵區別說明&#xff08;指令回調 vs 數據回調&#xff09;特性指令回調數據回調觸發場景授權/取消授權等管理事件通訊錄變更、應用菜單點擊等業務事件關鍵字段InfoTypeEvent ChangeType典型事件suite_auth, cancel_authchange_contact, suite_ticket響應要求必須返回加密的&…

LazyLLM教程 | 第2講:10分鐘上手一個最小可用RAG系統

貼心小梗概本文將介紹使用LazyLLM搭建最基礎的RAG的流程。首先介紹使用LazyLLM搭建RAG系統的必要環境配置&#xff0c;然后簡單回顧RAG的基本流程&#xff0c;接下來分別介紹RAG中文檔加載、檢索組件、生成組件三個關鍵部分的參數和基本使用方法&#xff0c;最后利用LazyLLM實現…

android9-PMS-常見問題及分析步驟

以下是基于 Android 9 的 Package Manager Service (PMS) 常見問題及分析步驟&#xff0c;結合系統原理與優化實踐整理&#xff1a; &#x1f527; 一、開機性能問題 現象 開機時間隨應用增多顯著延長&#xff0c;卡在“正在啟動應用”階段。 分析步驟 ① 確認掃描階段耗時adb…

生成模型實戰 | GLOW詳解與實現

生成模型實戰 | GLOW詳解與實現0. 前言1. 歸一化流模型1.1 歸一化流與變換公式1.2 RealNVP 的通道翻轉2. GLOW 架構2.1 ActNorm2.2 可逆 11 卷積2.3 仿射耦合層2.4 多尺度架構3. 使用 PyTorch 實現 GLOW3.1 數據處理3.2 模型構建3.3 模型訓練0. 前言 GLOW (Generative Flow) 是…

行業案例:杰和科技為智慧教育構建數字化硬件底座

清晨8點10分&#xff0c;深圳某學生踏入校園&#xff0c;智慧門閘識別身份&#xff0c;并同步發給家長&#xff1b;走廊里的“智慧班牌”向他們展示今日的課表&#xff1b;課堂上&#xff0c;教室前方的多媒體播放器里&#xff0c;老師引導學生學習“居民樓消防隱患”知識&…

Redis與MySQL數據同步:從“雙寫一致性”到實戰方案

Redis與MySQL數據同步&#xff1a;從“雙寫一致性”到實戰方案 在分布式系統中&#xff0c;Redis作為高性能緩存被廣泛使用——它能將熱點數據從MySQL中“搬運”到內存&#xff0c;大幅降低數據庫壓力、提升接口響應速度。但隨之而來的核心問題是&#xff1a;當MySQL數據更新時…

Java源碼構建智能名片小程序

在移動互聯網時代&#xff0c;紙質名片的局限性日益凸顯——信息更新不便、客戶管理困難、營銷效果難以追蹤。智能電子名片小程序以其便捷、高效、智能的特點&#xff0c;正成為商務人士的"數字營銷門戶"。而基于Java技術棧開發的智能名片系統&#xff0c;憑借其穩定…

如何在短時間內顯著提升3D效果圖渲染速度?

在建筑設計、游戲開發、影視制作等行業&#xff0c;3D效果圖的渲染速度是項目進度與效率的關鍵瓶頸。面對復雜場景時&#xff0c;漫長的渲染等待尤為突出。要在保證質量的前提下大幅縮短渲染時間&#xff0c;以下優化策略至關重要&#xff1a; 1. 升級硬件配置&#xff1a;渲染…

配置daemon.json使得 Docker 容器能夠使用服務器GPU【驗證成功】

&#x1f947; 版權: 本文由【墨理學AI】原創首發、各位讀者大大、敬請查閱、感謝三連 文章目錄&#x1f50d;你遇到的錯誤&#xff1a;&#x1f50d; 根本原因? 解決方案&#xff1a;正確安裝 NVIDIA Container Toolkit? 第一步&#xff1a;卸載舊版本&#xff08;如果存在&…

Linux 系統進程管理與計劃任務詳解

Linux 系統進程管理與計劃任務詳解 一、程序與進程的基本概念 程序&#xff1a;保存在外部存儲介質中的可執行機器代碼和數據的靜態集合。進程&#xff1a;在CPU及內存中處于動態執行狀態的計算機程序。關系&#xff1a;每個程序啟動后&#xff0c;可創建一個或多個進程。 二、…

【圖像處理】直方圖均衡化c++實現

直方圖均衡化是一種通過調整圖像像素灰度值分布&#xff0c;來增強圖像對比度的經典數字圖像處理技術。其核心在于將原始圖像的灰度直方圖從集中的某個區間“拉伸”或“均衡”到更廣泛的區間&#xff0c;讓圖像的明暗細節更清晰&#xff0c;關鍵在于利用累積分布函數實現灰度值…

Web前端實戰:Vue工程化+ElementPlus

1.Vue工程化 1.1介紹 模塊化&#xff1a;將js和css等&#xff0c;做成一個個可復用模塊組件化&#xff1a;我們將UI組件&#xff0c;css樣式&#xff0c;js行為封裝成一個個的組件&#xff0c;便于管理規范化&#xff1a;我們提供一套標準的規范的目錄接口和編碼規范&#xff0…

ECMAScript2021(ES12)新特性

概述 ECMAScript2021于2021年6月正式發布&#xff0c; 本文會介紹ECMAScript2021(ES12)&#xff0c;即ECMAScript的第12個版本的新特性。 以下摘自官網&#xff1a;ecma-262 ECMAScript 2021, the 12th edition, introduced the replaceAll method for Strings; Promise.any,…

Tlias 案例-整體布局(前端)

開發流程前端開發和后端開發是一樣的&#xff0c;都需要閱讀接口文檔。 準備工作&#xff1a; 1&#xff1a;導入項目中準備的基礎過程到 VsCode。2&#xff1a;啟動前端項目&#xff0c;訪問該項目3&#xff1a;熟悉一下基本的布局<script setup></script><tem…

三十二、【Linux網站服務器】搭建httpd服務器演示虛擬主機配置、網頁重定向功能

httpd服務器功能演示一、虛擬主機配置虛擬主機技術全景虛擬主機目錄規范1. 基于端口的虛擬主機&#xff08;8080/8081&#xff09;2. 基于IP的虛擬主機&#xff08;192.168.1.100/192.168.1.101&#xff09;3. 基于域名的虛擬主機&#xff08;site1.com/site2.com&#xff09;二…

串行化:MYSQL事務隔離級別中的終極防護

在現代應用程序中&#xff0c;數據的一致性和可靠性至關重要。想象一下&#xff0c;如果在一個銀行系統中&#xff0c;兩個用戶同時試圖轉賬到同一個賬戶&#xff0c;最終的數據結果可能會出乎意料。為了避免這種情況&#xff0c;MYSQL提供了不同的事務隔離級別&#xff0c;其中…

RAG:檢索增強生成的范式演進、技術突破與前沿挑戰

1 核心定義與原始論文 RAG&#xff08;Retrieval-Augmented Generation&#xff09;由Facebook AI Research團隊于2020年提出&#xff0c;核心思想是將參數化記憶&#xff08;預訓練語言模型&#xff09;與非參數化記憶&#xff08;外部知識庫檢索&#xff09;結合&#xff0c…

2024年藍橋杯Scratch10月圖形化stema選拔賽真題——旋轉的圖形

旋轉的圖形編程實現旋轉的圖形。具體要求1&#xff09;點擊綠旗&#xff0c;在舞臺上出現滑桿形式的變量 r&#xff0c;取值范圍為-1、0、1&#xff0c;默認值為 0&#xff0c;如圖所示&#xff1b;2&#xff09;1秒后&#xff0c;在舞臺上繪制出一個紅色正方形&#xff08;邊長…