C++中的單例模式的實現

1 什么是單例模式

單例模式 是一種創建型設計模式,確保一個類在整個程序生命周期中只有一個實例,并提供一個全局訪問點。

核心要求:

  1. 類不能被外部隨意創建(禁止 public 構造函數或限制實例數量)。
  2. 不能被復制或移動。
  3. 提供一個全局訪問接口(如?getInstance())。
  4. 實例的生命周期通常伴隨整個程序。

2 模版單例的實現

2.1 單例模版GSingleton<T>

  • 使用場景:項目中需要大量單例,且希望統一管理時,使用單例模版會提高代碼復用。單例模版類本身不能直接使用,而是作為基類被繼承。需要實現單例的類只需要繼承GSingleton即可。

  • 注意:模版基類GSingleton的構造函數和析構函數必須是protected修飾,不能用private,因為派生類在構造時或者析構時可以調用。而派生類T的構造函數和析構函數可以由private修飾。

template<typename T>
class GSingleton
{
protected:GSingleton() = default;~GSingleton() = default;  // 非虛,除非你真的需要// 禁止拷貝和移動GSingleton(const GSingleton&) = delete;GSingleton& operator=(const GSingleton&) = delete;GSingleton(GSingleton&&) = delete;GSingleton& operator=(GSingleton&&) = delete;public:static T& getInstance() {static T instance;return instance;}
};
  • 模版類詳細介紹
  1. protect:修飾的方法或者成員變量只能被本類、派生類以及友元訪問。
  2. Gsingleton<T>是基類,必須是protected,這樣派生類在構造時才可以調用父類的構造函數。
  3. GSingleton = default:表示顯式要求編譯器生成一個默認構造函數(無參構造函數)
  4. ~GSingleton() = default:表示讓編譯器生成默認析構函數。
  5. GSingleton(const GSingleton&) = delete:這是對拷貝構造函數的聲明,禁止拷貝。
    const GSingleton&:表示對另一個GSingleton對象的常量引用。
    delete表示這個函數被顯示刪除,不能使用。
    Access a;
    Access b(a);        // ? 錯誤!調用拷貝構造函數
    Access c = a;       // ? 錯誤!也是拷貝構造(賦值語法,實際是構造)
    
  6. GSingleton& operator=(const GSingleton&) = delete:這是對拷貝賦值運算符的聲明,禁止拷貝。
    operator=:表示賦值操作符的重載。
    Access a;
    Access b;
    b = a;              // ? 錯誤!調用拷貝賦值
  7. GSingleton(GSingleton&&) = delete:這是移動構造函數的聲明,禁止移動構造。
    GSingleton&&:是一個右值引用,表示臨時對象或者可移動的對象。
    移動不是拷貝,而是資源轉移,將一個臨時對象所擁有的資源轉移給另一個對象,而原對象不再擁有該資源。
    移動避免了不必要的內存分配和數據賦值,因此非常高效。
    Access a;
    Access b(std::move(a));  // ? 錯誤!試圖移動構造(即使 NRVO 優化,語義上也不允許)
  8. GSingleton& operator=(GSingleton&&) = delete:這是移動賦值運算符,禁止將一個臨時對象移動賦值給已有對象。
    Access a;
    Access b;
    b = std::move(a);   // ? 錯誤!禁止移動賦值

??總結

代碼禁止的操作
Access(const Access&) = delete;Access b(a);?或?Access b = a;
Access& operator=(const Access&) = delete;b = a;
Access(Access&&) = delete;Access b(std::move(a));
Access& operator=(Access&&) = delete;b = std::move(a);

2.2 使用單例模版定義派生類

class GLoggerImpl : public GSingleton<GLoggerImpl>
{
private:GLoggerImpl();friend class GSingleton<GLoggerImpl>;
public:~GLoggerImpl();void WriteLog(int logLevel, const char* file, int line, const char* function, const char* format, va_list args);
}

注意:在聲明GloggerImpl的類時使用了friend class GSingleton<GLoggerImpl>,表示聲明讓模版類成為友元。

原因:在調用GLoggerImpl::getInstance()時,

// template<typename T> class GSingleton 
static T& getInstance() {static T instance;  // ← 這里要構造 T,即 GLoggerImplreturn instance;
}

static T instance會調用GLoggerImpl的構造函數,由于GLoggerImpl()是private或者protected,而GSingleton<GLoggerImpl>模版類不是GLoggerImpl類的成員變量,默認無法訪問GLoggerImpl的構造函數,因此要聲明friend來顯示授權。

友元:是C++的一個關鍵字,它允許一個類、函數或者模版訪問另一個類的private和protected成員,即使它不是那個類的成員函數或者派生類。

  • friend可以修飾函數,稱為友元函數,可以在一個類中聲明友元函數,這樣該函數可以訪問該類的私有成員變量或者成員函數。
  • friend可以修飾類,成為友元類,可以訪問所在類中的private或者protect的成員,友元關系是單向的。
  • friend可以修飾一個模版類或者模版函數,成為友元模版。例如GloggerImpl中friend修飾的模版。

2.3 單例類的使用

由于GLoggerImpl繼承了GSingleton的模版基類,因此繼承了父類靜態的getInstance()方法。所以通過GLoggerImpl::getInstance()可以獲取該類唯一的單例對象。

void LogD(const char* file, int line, const char* function, const char* format, ...) {va_list args;va_start(args, format);GLoggerImpl::getInstance()->WriteLog(LEVEL_DEBUG, file, line, function, format, args);va_end(args);
}

3 單例類的實現

使用場景:項目里只需要少數單例,且追求高可讀性和維護性,推薦單例類的實現。

該實現是線程安全的,使用靜態局部變量方式實現。

class Singleton {
public:static Singleton& getInstance() {static Singleton instance;  // C++11 起,線程安全return instance;}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;Singleton(Singleton&&) = delete;Singleton& operator=(Singleton&&) = delete;private:Singleton() = default;~Singleton() = default;
};

使用:Singleton s = Singleton::getInstance()就獲取該類的唯一單例。

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

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

相關文章

匯編基礎1

1.格式偽操作&#xff1a;它們不是ARM處理器實際的指令&#xff08;如MOV&#xff0c; ADD等&#xff09;&#xff0c;而是寫給匯編器看的命令&#xff0c;用于指導匯編器如何工作area reset, code, readonlycode32entry內容 endarea: 這是最重要的一個偽操作&#xff0c;用…

設計模式(C++)詳解—單例模式(2)

<摘要> 單例模式是創建型設計模式中最簡單但應用最廣泛的模式之一&#xff0c;它確保一個類只有一個實例并提供全局訪問點。本文從歷史背景和核心概念出發&#xff0c;系統闡述了單例模式的產生緣由和演進脈絡&#xff0c;深入剖析了其在資源管理、狀態一致性和訪問控制方…

kafka如何保證消息的順序性

kafka如何保證消息的順序性 Kafka只能在分區&#xff08;Partition&#xff09;級別保證消息的順序性&#xff0c;而不能在主題&#xff08;Topic&#xff09;級別保證全局順序。 核心原理&#xff1a;分區和偏移量分區&#xff08;Partition&#xff09;是順序性的基礎&#x…

傳輸層:UDP/TCP協議

網絡協議圖 一.UDP 特點: 無連接&#xff0c;不可靠&#xff0c;面向數據報&#xff0c;全雙工&#xff08;前面網絡編程中介紹過&#xff09; 格式: 服務器的端口號一般都是程序員指定的(這樣你才能訪問到),客戶端的端口號是系統自動分配的(如果提前指定好, 可能會與其他程…

A/B測試全解析:原理、流程與實戰案例

A/B測試&#xff08;AB Testing&#xff09;原理與實踐全解析 在數據驅動的時代&#xff0c;A/B測試幾乎是每一個互聯網公司都會使用的實驗方法。無論是電商平臺優化轉化率&#xff0c;還是內容平臺提升點擊率&#xff0c;抑或是游戲公司提升留存&#xff0c;A/B測試都是最常見…

循環神經網絡(三):小練習

RNN小練習 要求&#xff1a; 假設有 4 個字 吃 了 沒 &#xff1f;&#xff0c;請使用 torch.nn.RNN 完成以下任務 將每個進行 one-hot 編碼請使用 吃 了 沒 作為輸入序列&#xff0c;了 沒 &#xff1f; 作為輸出序列RNN 的 hidden_size 64請將 RNN 的輸出使用全連接轉換成 4…

ESPIDF官方文檔,啟用dhcp會禁用對應的STA或AP的靜態IP,我測試STA確實是,但是AP不是,為什么

1. STA 模式下的 DHCP&#xff08;客戶端角色&#xff09;ESP32 當 Station&#xff08;STA&#xff09; 時&#xff0c;它的行為就跟你的手機/筆記本連 Wi-Fi 一樣&#xff1a;DHCP 客戶端 → 去路由器&#xff08;DHCP 服務器&#xff09;要一個 IP。特點啟用 DHCP&#xff0…

cocos2d. 3.17.2 c++如何實現下載斷點續傳zip壓縮包帶進度條

新建類CurlDown #include “curl/curl.h” #include using namespace std; USING_NS_CC; /** 資源下載curl */ class CurlDown { public: CurlDown(); ~CurlDown(); void StartDownResZip(string downLoadUrl, int64_t totalSize); //下載控制 void downloadControler(); //下…

MySQL 整型數據類型:選對數字類型,讓存儲效率翻倍

MySQL 整型數據類型&#xff1a;選對數字類型&#xff0c;讓存儲效率翻倍 在 MySQL 中&#xff0c;整型&#xff08;整數類型&#xff09;是最常用的數據類型之一&#xff0c;從用戶 ID 到商品數量&#xff0c;幾乎所有涉及數字的場景都離不開它。但你知道嗎&#xff1f;選對整…

公司電腦監控軟件有哪些?公司電腦監控軟件應該怎么選擇

大家好呀&#xff0c;電競直播運營團隊常常面臨 “直播腳本被抄襲、用戶付費數據篡改、主播話術外泄” 的問題&#xff01;尤其是獨家直播流程腳本、用戶充值記錄、主播互動話術庫、賽事解說手稿&#xff0c;一旦泄露可能導致競品跟風、用戶信任下降、直播競爭力減弱&#xff5…

ARM裸機開發:鏈接腳本、進階Makefile(bsp)、編譯過程、beep實驗

一、鏈接腳本的作用&#xff1f;各個段存放什么數據類型&#xff08;一&#xff09;鏈接腳本內容SECTIONS {. 0x87800000;.text : {obj/start.o*(.text)}.rodata ALIGN(4) : {*(.rodata*)}.data ALIGN(4) : {*(.data)}__bss_start .;.bss ALIGN(4) : {*(.bss) *(COMMON)}__bs…

Linux驅動開發(1)概念、環境與代碼框架

一、驅動概念驅動與底層硬件直接打交道&#xff0c;充當了硬件與應用軟件中間的橋梁。1、具體任務&#xff08;1&#xff09;讀寫設備寄存器&#xff08;實現控制的方式&#xff09;&#xff08;2&#xff09;完成設備的輪詢、中斷處理、DMA通信&#xff08;CPU與外設通信的方式…

計算機視覺(十):ROI

什么是感興趣區域&#xff08;ROI&#xff09;&#xff1f; 在計算機視覺中&#xff0c;**感興趣區域&#xff08;ROI&#xff09;**指的是圖像中包含我們想要分析、處理或識別的目標或特征的特定子集。就像我們在閱讀一本書時會聚焦于某個重要的段落&#xff0c;計算機視覺系統…

Jenkins 構建 Node 項目報錯解析與解決——pnpm lockfile 問題實戰

在使用 Jenkins 自動化構建 Node.js 項目時&#xff0c;經常會遇到類似報錯&#xff1a; ERR_PNPM_OUTDATED_LOCKFILE? Cannot install with "frozen-lockfile" because pnpm-lock.yaml is not up to date with package.json Error: Cannot find module node_module…

Kafka在多環境中安全管理敏感

1. 配置提供者是什么&#xff1f; 配置提供者&#xff08;ConfigProvider&#xff09;是一類按需“拉取配置”的組件&#xff1a;應用讀取配置時&#xff0c;按約定的占位符語法去外部來源&#xff08;目錄、環境變量、單一 properties 文件、你自定義的來源……&#xff09;取…

編程工具的演進邏輯:從Python IDLE到Arduino IDE的深度剖析

引言:工具進化的本質 在編程學習與開發的道路上,我們總會與各種各樣的工具相遇。一個有趣的現象是,無論是初學者的第一款工具Python IDLE,還是硬件愛好者常用的Thonny和Arduino IDE,它們都自稱“集成開發環境”(IDE)。這背后隱藏著怎樣的邏輯? 本文將帶你深入分析這三…

p10k configure執行報錯: ~/powerlevel10k/config/p10k-lean.zsh is not readable

[ERROR] p10k configure: ~/powerlevel10k/config/p10k-lean.zsh is not readable 背景 我移動了Powerlevel10k文件夾的位置&#xff0c;導致p10k configure命令找不到powerlevel10k文件夾的位置。 原來Powerlevel10k的位置&#xff1a;~/powerlevel10k 移動后Powerlevel10k的位…

Java 學習筆記(進階篇3)

1. 美化界面關鍵邏輯 1&#xff1a;// 相對路徑&#xff1a;直接從項目的 src 目錄開始寫&#xff0c;不包含 D:\ 和個人名字 ImageIcon bg new ImageIcon("src/image/background.png"); JLabel background new JLabel(bg);這兩行代碼是 Swing 中加載并顯示圖片的經…

BFD 概述

BFD簡介1.BFD:Bidirectional Forwarding Detection,雙向轉發檢查概述&#xff1a;毫秒級鏈路故障檢查&#xff0c;通常結合三層協議&#xff08;如靜態路由、vrrp、 ospf、 BGP等&#xff09;實現鏈路故障快速切換。作用&#xff1a;① 檢測二層非直連故障② 加快三層協議收斂底…

【嵌入式DIY實例-ESP32篇】-Flappy Bird游戲

Flappy Bird游戲 文章目錄 Flappy Bird游戲 1、游戲介紹 2、硬件準備與接線 3、代碼實現 《Flappy Bird》游戲以其引人入勝的玩法和簡約的設計風靡全球。本文將探討如何使用 OLED SSD1306 顯示屏和 ESP32 微控制器重現這款經典游戲。這個 DIY 項目不僅充滿樂趣,也是學習編程和…