C++單例模式精解

單例模式(重點*)

單例模式是23種常用設計模式中最簡單的設計模式之一,它提供了一種創建對象的方式,確保只有單個對象被創建。這個設計模式主要目的是想在整個系統中只能出現類的一個實例,即一個類只有一個對象。

將單例對象創建在靜態區

根據已經學過的知識進行分析:

  1. 將構造函數私有;

  2. 通過靜態成員函數getInstance創建局部靜態對象,確保對象的生命周期和唯一性;

  3. getInstance的返回值設為引用,避免復制;

    image-20240308174000886

    image-20240308174029507

隱患:如果單例對象所占空間較大,可能會對靜態區造成內存壓力。

class Point
{
public://定義為靜態函數是因為要創建對象而調用成員函數又需要對象來調用,所以就將成員函數定義為靜態成員函數直接使用類來進項調用// 使用& 是因為防止返回發生拷貝構造static Point & getInstance(){static Point pt(1,2);return pt;}void print() const{cout << "(" << this->_ix<< "," << this->_iy<< ")" << endl;}private:Point(int x,int y): _ix(x), _iy(y){cout << "Point(int,int)" << endl;}
private:int _ix;int _iy;
};void test0(){//使用&來接收就不會發生拷貝構造//這是pt和pt2指向就是相同的Point & pt = Point::getInstance();pt.print();Point & pt2 = Point::getInstance();pt2.print();cout << &pt << endl;cout << &pt2 << endl;
}

將單例對象創建在堆區

既然將單例對象創建在全局/靜態區可能會有內存壓力,那么為這個單例對象動態分配空間是比較合理的選擇。請嘗試實現代碼:

分析:
  1. 構造函數私有;

  2. 因為非靜態對象沒有唯一性,所以我們要人為加一個判斷語句來看是否第一次調用getInstance函數,所以在類中聲明一個靜態成員(因為我們想用它在靜態成員函數中做判斷)來接收通過靜態成員函數getInstance創建堆上的對象,返回Point*類型的指針,如果該靜態成員_pInstance為空就可以創建對象;

  3. 通過靜態成員函數完成堆對象的回收。

    image-20240308181905940

    image-20240308181713168

    ? ? ? ? ? ? 多個指針指向同一塊空間,是比較危險的,如果我像上面代碼那樣將代碼進行回收了,我在用其他指向原來那塊空間的指針來訪問就會出問題,所以為了避免問題,就使用下面的單例模式的規范。

image-20240308181807966

可能會對析構函數產生誤解,析構函數是用來回收數據成員申請的堆空間的,而上面的數據成員并沒有申請堆空間。

假如是將代碼加到析構函數中使用析構函數來回收空間會怎么樣呢?

調用析構函數,進入if判斷不為nullptr,調用delete,而delete的第一步又是調用析構函數,這樣就進去無限的循環直到棧的空間被占滿。

這里不使用注釋那樣來調用destory是因為destory一開始設計不是static,需要對象來調用,而且這也還會再一次調用創建對象的成員函數,所以直接將destory設計為static,直接使用類名來調用。

上面定義的是一個死的數據因為是單例模式只能讓他進行一次初始化,我們如果想要使單例的數據可以修改呢?

我們可以將上面的getInstance的構造函數改為無參構造只進行空間的申請,不對空間的數據進行初始化,然后通過這個函數的返回值再次調用初始化數據函數(init),使數據可以進行修改。

image-20240309100738528

單例對象的數據成員申請堆空間

要求:實現一個單例的Computer類,包含品牌和價格信息。
#include <string.h>
#include <iostream>
using std ::cout;
using std ::endl;
class Computer
{
public:static Computer *getInstance(){if (_pInstance == nullptr)_pInstance = new Computer();return _pInstance;}static void destroy(){if (_pInstance){delete _pInstance;_pInstance = nullptr;}cout << "heap delete" << endl;}void init(const char *brand, double price){if (_brand){delete[] _brand;_brand = nullptr;}_brand = new char[strlen(brand) + 1]();strcpy(_brand, brand);_price = price;}void print(){cout << _brand << endl;cout << _price << endl;}private:// 構造函數Computer() {};Computer(const char *brand, double price): _brand(new char[strlen(brand) + 1]()), _price(price){strcpy(_brand, brand);}// 析構函數~Computer(){if (_brand){delete _brand;_brand = nullptr;}cout << "~Computer" << endl;}Computer(const Computer &rhs) = delete;Computer &operator=(const Computer &rhs) = delete;char *_brand;double _price;static Computer *_pInstance;
};
Computer *Computer ::_pInstance = nullptr;int main()
{Computer ::getInstance()->init("bob", 2222);Computer ::getInstance()->print();Computer ::getInstance()->init("tom", 6666);Computer ::getInstance()->print();Computer ::destroy();return 0;
}

image-20240309102850728

image-20240309102833694

單例模式的應用場景

1、有頻繁實例化然后銷毀的情況,也就是頻繁的 new 對象,可以考慮單例模式;

2、創建對象時耗時過多或者耗資源過多,但又經常用到的對象;

3、當某個資源需要在整個程序中只有一個實例時,可以使用單例模式進行管理(全局資源管理)。例如數據庫連接池、日志記錄器等;

4、當需要讀取和管理程序配置文件時,可以使用單例模式確保只有一個實例來管理配置文件的讀取和寫入操作(配置文件管理);

5、在多線程編程中,線程池是一種常見的設計模式。使用單例模式可以確保只有一個線程池實例,方便管理和控制線程的創建和銷毀;

6、GUI應用程序中的全局狀態管理:在GUI應用程序中,可能需要管理一些全局狀態,例如用戶信息、應用程序配置等。使用單例模式可以確保全局狀態的唯一性和一致性。

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

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

相關文章

【微服務】java中http調用組件深入實戰詳解

目錄 一、前言 二、http調用概述 2.1 什么是http調用 2.1.1 http調用步驟 2.2 HTTP調用特點 2.3 HTTP調用應用場景 三、微服務場景下http調用概述 3.1 微服務開發中http調用場景 3.2 微服務組件中http的應用 四、常用的http調用組件 4.1 java中常用的http組件介紹 4…

Implementing SAP BPC Embedded - 2nd Edition

Implementing SAP BPC Embedded - 2nd Edition

stm32第四天控制蜂鳴器

一&#xff1a; 1.蜂鳴器的種類 蜂鳴器是一種常用的電子發聲元器件&#xff0c;采用直流電壓供電。廣泛應用于計算機&#xff0c;打ED機&#xff0c;報警器&#xff0c;電子玩具&#xff0c;汽車電子設備燈等產品中常見的蜂鳴器可分為有源蜂鳴器和無源蜂鳴器。 2.蜂鳴器的控制…

Swift 中 associatedtype 的用法詳解

目錄 前言 1.什么是associatedtype 2.associatedtype 的作用 1.讓協議支持泛型 2.讓協議支持不同的數據類型 3.結合 where 關鍵字限制類型 4.什么時候使用 associatedtype 5.總結 前言 在 Swift 語言中&#xff0c;泛型&#xff08;Generics&#xff09;是一個非常強大…

每日Attention學習26——Dynamic Weighted Feature Fusion

模塊出處 [ACM MM 23] [link] [code] Efficient Parallel Multi-Scale Detail and Semantic Encoding Network for Lightweight Semantic Segmentation 模塊名稱 Dynamic Weighted Feature Fusion (DWFF) 模塊作用 雙級特征融合 模塊結構 模塊思想 我們提出了 DWFF 策略&am…

OpenCV實現圖像特征提取與匹配

?一、特征檢測與描述子提取? ?選擇特征檢測器? 常用算法包括&#xff1a; ?ORB?&#xff1a;一種高效的替代SIFT和SURF的算法&#xff0c;主要用于移動機器人和增強現實等領域。適合實時應用&#xff0c;結合FAST關鍵點與BRIEF描述子?。?SIFT&#xff08;尺度不變特征變…

向量檢索在AI中的應用與技術解析

關鍵要點 向量檢索在AI中用于信息檢索、推薦系統和圖像搜索&#xff0c;研究表明其通過高維空間中的向量表示數據來提升搜索相關性。它依賴于嵌入技術&#xff08;如Word2Vec、BERT&#xff09;和近鄰算法&#xff08;如kNN、ANN&#xff09;&#xff0c;證據傾向于其在處理大…

事務與異步方法(@Async)協同工作

目錄 1. 問題場景與風險 &#xff08;1&#xff09;典型場景 &#xff08;2&#xff09;風險分析 2. 解決方案&#xff1a;事務提交后觸發異步操作 &#xff08;1&#xff09;代碼示例 &#xff08;2&#xff09;關鍵注解 3. 原理解析 &#xff08;1&#xff09;事務同…

關于進程的實驗(子進程和父進程相關的)

文章目錄 1.第一個問題2.第二個問題3.第三個問題 1.第一個問題 編寫一段程序&#xff0c;利用系統調用fork( )創建兩個進程。當此程序運行時&#xff0c;在系統中有一個父進程和兩個子進程活動。讓每一個進程在屏幕上顯示一個字符&#xff1a;父進程顯示字符“a”;子進程分別顯…

MyBatis 如何創建 SqlSession 對象的?

MyBatis 創建 SqlSession 對象的過程主要由 SqlSessionFactory 接口及其實現類來完成。以下是詳細步驟&#xff1a; 1. SqlSessionFactory 接口: SqlSessionFactory 是 MyBatis 的核心接口之一&#xff0c;它負責創建 SqlSession 對象。 你可以將 SqlSessionFactory 視為 Sql…

深度優先搜索(DFS)剪枝技術詳解與C++實現

深度優先搜索&#xff08;DFS&#xff09;剪枝技術通過提前終止無效路徑的搜索&#xff0c;大幅提升算法效率。以下是五種核心剪枝技術的詳細解析及C代碼示例&#xff1a; 目錄 一、可行性剪枝 C實現示例 二、搜索順序剪枝 偽代碼邏輯 三、最優性剪枝 C實現示例 四、排除…

【雙指針】移動零

題目描述&#xff1a; 算法分析&#xff1a; 觀察輸入輸出&#xff1a; 輸出中一共分為兩個區域&#xff0c;0區和非零區。 但是在處理未完成之前&#xff0c;必然存在著一個零和非零數共存的區域&#xff0c;所以在處理的過程當中一共有三個區域&#xff0c;0區&#xff0c;…

學習15天:pytest

1、.pytest強大的插件 pytest-html(生成html格式的自動化測試報告) pytest-xdist測試用例分布式執行。多CPU分發。 pytest-ordering 用于改變測試用例的執行順序 pytest-rerunfailures用例失敗后重跑 allure-pytest 用于生成美觀的測試報告。 2、規則&#xff1a; 模塊…

股票交易所官方api接口有哪些?獲取和使用需要滿足什么條件

炒股自動化&#xff1a;申請官方API接口&#xff0c;散戶也可以 python炒股自動化&#xff08;0&#xff09;&#xff0c;申請券商API接口 python炒股自動化&#xff08;1&#xff09;&#xff0c;量化交易接口區別 Python炒股自動化&#xff08;2&#xff09;&#xff1a;獲取…

2.7 滑動窗口專題:串聯所有單詞的子串

LeetCode 30. 串聯所有單詞的子串算法對比分析 1. 題目鏈接 LeetCode 30. 串聯所有單詞的子串 2. 題目描述 給定一個字符串 s 和一個字符串數組 words&#xff0c;words 中所有單詞長度相同。要求找到 s 中所有起始索引&#xff0c;使得從該位置開始的連續子串包含 words 中所…

【區塊鏈】區塊鏈密碼學基礎

&#x1f308;個人主頁: 鑫寶Code &#x1f525;熱門專欄: 閑話雜談&#xff5c; 炫酷HTML | JavaScript基礎 ?&#x1f4ab;個人格言: "如無必要&#xff0c;勿增實體" 文章目錄 區塊鏈密碼學基礎引言一、哈希函數1.1 基本概念1.2 數學表達 二、非對稱加密2.1…

Spring Boot配置類原理、Spring Boot核心機制理解,以及實現自動裝置的底層原理

目的:從底層源碼角度分析 Spring Boot 配置類以及自動裝載的底層原理 文章目錄 1. Spring Boot 配置類實現自動裝載1.1 @Configuration注解1.2 @Configuration 注解完成 bean 注入流程圖1.3 @ConfigurationProperties注解賦值2. Spring Boot的核心機制:自動裝配2.1 @SpringBo…

docker桌面版啟動redis,解決無法連接

docker run -d --name redis -p 6379:6379 -v E:\2\redis\redis.conf:/usr/local/etc/redis/redis.conf redis redis-server /usr/local/etc/redis/redis.conf 在本地創建一個目錄&#xff0c;里面有個redis.conf文件&#xff0c;內容如下&#xff0c;啟動時綁定這個配置文件目…

[網絡][tcp協議]:tcp報頭

tcp(傳輸控制協議)是一種面向字節流的傳輸層協議,相較于udp協議,tcp能保證傳輸數據的可靠性與準確性,tcp也是目前最常見的傳輸層協議 本文主要介紹tcp報頭各個字段的含義與用途 注:保留6位和6位標記位是目前最普遍的寫法,在我查資料時,發現有一些拓展情況,會在后文細說 最簡單的…

【虛幻C++筆記】引擎源碼下載及編譯步驟

目錄 1.在GitHub上訪問虛幻引擎源代碼2.安裝Visual Studio 20223.解壓完成以后&#xff0c;打開源碼的根目錄&#xff0c;選擇Setup.bat運行4.選擇GenerateProjectFiles.bat運行,生成uE5.sln文件&#xff0c;點擊這個文件打開項目5.設置編譯的選項&#xff0c;選擇DevelopmentE…