智能指針之設計模式1

本文探討一下智能指針和GOF設計模式的關系,如果按照設計模式的背后思想來分析,可以發現圍繞智能指針的設計和實現有設計模式的一些思想體現。當然,它們也不是嚴格意義上面向對象的設計模式,畢竟它們沒有那么分明的類層次體系,和GOF經典設計模式在外在形式上有所差別,重點是理解設計模式的思想在它們身上的體現,以及怎樣幫助它們實現意圖的。

限于篇幅,分成了幾篇文章來介紹,先從對象的創建開始。

1、工廠模式

工廠模式是把創建對象和使用對象的職責分離了,它們可以控制對象的創建過程,封裝了創建細節,讓用戶不再關心具體對象的創建過程。

C++標準庫提供了一些輔助函數和輔助類來創建智能指針對象,它們都是工廠方法或者工廠類。創建unique_ptr和shared_ptr除了使用常規的構造函數之外,還提供了三個工廠方法:make_shared()、make_unique()和allocate_shared(),它們都是簡單工廠方法;此外,為了能夠從一個shared_ptr對象的內部,通過this指針創建一個shared_ptr對象,工廠類enable_shared_from_this<T>還提供了成員函數shared_from_this()作為工廠方法。

那么,使用工廠模式創建智能指針對象有什么好處呢?

首先,通過工廠方法可以給創建過程起一個富有表達力、自解釋的名稱,能有效地幫助程序員容易使用,甚至無需提供閱讀文檔接口,而類的構造函數必須和類名完全一樣,無法能夠見文知意。make_shared()和make_unique(),一看就知道是創建shared_ptr和unique_ptr對象,更具特色的是shared_from_this(),通過它的名稱就應該知道這個函數是通過this指針來創建shared_ptr對象,既然有this字眼,也能知道應該是在一個類的內部使用。

其次,關注點分離,分離了創建智能指針和使用智能指針的職責,讓程序員不再關心智能指針對象的創建過程,不再關心是如何創建出來的,程序員把關注點放在智能指針的使用上面,減輕了程序員的心智負擔。有人說,C++有了智能指針之后,就不應該在程序中出現new和delete了,可能說的絕對了點,但顯然工廠方法make_shared()和make_unique()給了他這樣說的底氣。

再者,工廠模式可以控制智能指針對象的創建過程,這也是核心意圖,它的作用有下面幾點:

1、保證在堆中創建資源對象
智能指針缺省要求管理的資源對象是在堆中創建的對象,如果把一個指向棧上創建的對象的指針,讓unique_ptr或shared_ptr去管理,最后在析構時會發生異常,顯然是不對的。如何避免程序員無意中犯這樣錯誤?那么作為控制對象創建過程的工廠模式,用在這兒再合適不過了。由工廠方法來控制智能指針對象的創建過程,程序員在make_unique和make_shared工廠函數中只傳遞創建資源對象相關參數就行了,即保證智能指針管理的肯定是使用new操作符創建的對象,這樣就保證了程序的安全性。

2、保證創建過程中資源對象不泄露
make_unique()和make_shared()能夠保證資源對象的釋放安全,我們稍加留意就會發現,無論是shared_ptr類還是unique_ptr類,在它們的構造函數中都沒有同時初始化對象資源,對象資源是在外部使用new操作符在堆上創建之后,以裸指針的形式作為構造函數的參數來創建智能指針對象,也就是說資源對象的創建和智能指針對象的創建,它們不是一體的,它們之間是有空隙的,如果在這個間隙中有別的代碼運行,并發生了異常,可能會造成資源泄漏。比如(這個例子來自Effective Modern C++ 條款21):

processWidget(std::shared_ptr<Widget>(new Widget), computePriority());

因為編譯器在編譯時,可能是按照下面的順序生成代碼:1、實施“new Widget”,2、執行computePriority(),3、運行std::shared_ptr構造函數。如果生成了這樣的代碼,并且在運行時computePriority()發生了異常,那么在第1步動態分配的Widget對象會被泄露,因為它沒有機會被存儲到第3步才接管它的shared_ptr對象中去。如果使用make_shared()工廠方法來創建shared_ptr對象,就不會有潛在的資源泄露風險了:

processWidget(make_shared<Widget>(), computePriority());

使用make_unique和make_shared讓unique_ptr和shared_ptr在創建對象時就同時獲取了資源,即獲取資源即初始化(符合了RAII慣例的字面意思)。

3、創建時優化內存空間布局
工廠方法make_shared()在創建shared_ptr對象時,還可以對內存布局進行優化。shared_ptr對象包含了兩個指針成員,一個指向資源對象,一個指向控制塊,需要進行兩次new操作才能初始化完,在訪問時需要分別進行兩次指針解引用。如果資源對象和控制塊分配在同一個內存塊中,這樣就有更好的空間局部性,對cache更友好。在make_shared()內部可以進行控制這個實現過程,把控制塊的大小與資源的大小的和作為分配內存空間的大小,new一次就行了,然后分別讓資源對象指針和控制塊指針分別指向它們所在的位置,并初始化。讓內存空間更緊湊,節省了內存空間,同時因為有更好的cache局部性,也提高了訪問速度。

4、保證安全創建智能指針對象
weak_ptr類的lock()成員函數也是創建shared_ptr對象的一個工廠方法,控制的是從一個還沒有銷毀資源對象的shared_ptr對象中創建另一個shared_ptr對象。在多線程環境下,增加shared_ptr對象的引用計數和把控制塊指針、資源對象指針作為參數創建shared_ptr對象時,它們不是原子操作,在多線程下會存在data race。因此,在創建時需要保證線程安全,顯然交給程序員在外面實現是不現實的,那就把它封裝在一個工廠方法中,讓它來控制shared_ptr的創建過程,保證創建過程的線程安全。

enable_shared_from_this<T>類的成員函數shared_from_this()也是創建shared_ptr對象的一個工廠方法,控制的是通過資源對象的this指針來創建shared_ptr對象。它保證了是從一個已有的shared_ptr對象中創建的,如果不是,則會拋出異常;同時也保證了不會發生同一個this指針被多個不同shared_ptr對象管理生存期的錯誤,否則,如果用戶在外部隨便把this指針作為參數去調用shared_ptr構造函數,可能是重復管理,而發生錯誤。

最后,智能指針是裸指針的包裝類,而裸指針又指向資源對象,控制創建智能指針對象的過程,實際上也是在控制創建資源對象的過程。例如,工廠模式在控制智能指針對象的創建過程同時,也控制了資源對象使用new在堆上創建,如make_unique()和make_shared(),也控制了shared_ptr對象從this指針創建的過程,如shared_from_this()。

工廠模式控制了智能指針和資源對象的創建過程,那么銷毀工作又是如何實現的呢?下一篇文章繼續介紹。

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

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

相關文章

中間件--ClickHouse-1--基礎介紹(列式存儲,MPP架構,分布式計算,SQL支持,向量化執行,億萬級數據秒級查詢)

1、概述 ClickHouse是一個用于聯機分析(OLAP)的列式數據庫管理系統(DBMS)。它由俄羅斯的互聯網巨頭Yandex為解決其內部數據分析需求而開發&#xff0c;并于2016年開源。專為大規模數據分析&#xff0c;實時數據分析和復雜查詢設計&#xff0c;具有高性能、實時數據和可擴展性等…

Go之Slice和數組:深入理解底層設計與最佳實踐

在Go語言中&#xff0c;數組&#xff08;Array&#xff09;和切片&#xff08;Slice&#xff09;是兩種看似相似卻本質不同的數據結構。本文將深入剖析它們的底層實現機制&#xff0c;并結合實際代碼示例&#xff0c;幫助開發者掌握核心差異和使用場景。 一、基礎概念&#xff…

力扣熱題100——普通數組(不普通)

普通數組但一點不普通&#xff01; 最大子數組和合并區間輪轉數組除自身以外數組的乘積缺失的第一個正數 最大子數組和 這道題是非常經典的適用動態規劃解決題目&#xff0c;但同時這里給出兩種解法 動態規劃、分治法 那么動態規劃方法大家可以在我的另外一篇博客總結中看到&am…

矩陣基礎+矩陣轉置+矩陣乘法+行列式與逆矩陣

GPU渲染過程 矩陣 什么是矩陣&#xff08;Matrix&#xff09; 向量 &#xff08;3&#xff0c;9&#xff0c;88&#xff09; 點乘&#xff1a;計算向量夾角 叉乘&#xff1a;計算兩個向量構成平面的法向量。 矩陣 矩陣有3行&#xff0c;2列&#xff0c;所以表示為M32 獲取固…

MySQL之text字段詳細分類說明

在 MySQL 中&#xff0c;TEXT 是用來存儲大量文本數據的數據類型。TEXT 類型可以存儲非常長的字符串&#xff0c;比 VARCHAR 類型更適合存儲大塊的文本數據。TEXT 數據類型分為以下幾個子類型&#xff0c;每個子類型用于存儲不同大小范圍的文本數據&#xff1a; TINYTEXT: 可以…

超詳細!Android 面試題大匯總與深度解析

一、Java 與 Kotlin 基礎 1. Java 的多態是如何實現的&#xff1f; 多態是指在 Java 中&#xff0c;同一個行為具有多個不同表現形式或形態的能力。它主要通過方法重載&#xff08;Overloading&#xff09;和方法重寫&#xff08;Overriding&#xff09;來實現。 方法重載&a…

如何提高webrtc操作跟手時間,降低延遲

第一次做webrtc項目&#xff0c;操作延遲&#xff0c;一直是個問題&#xff0c;多次調試都不能達到理想效果。偶爾發現提高jitterBuffer時間可以解決此問題。關鍵代碼 const _setJitter (values: number) > { const receives peerConnection.getReceivers();receives.f…

語音合成(TTS)從零搭建一個完整的TTS系統-第一節-效果演示

一、概述 語音合成又叫文字轉語音&#xff08;TTS-text to speech &#xff09;&#xff0c;本專題我們記錄從零搭建一個完整的語音合成系統&#xff0c;包括文本前端、聲學模型和聲碼器&#xff0c;從模型訓練到系統的工程化實現&#xff0c;模型可以部署在手機等嵌入式設備上…

實驗三 I/O地址譯碼

一、實驗目的 掌握I/O地址譯碼電路的工作原理。 二、實驗電路 實驗電路如圖1所示&#xff0c;其中74LS74為D觸發器&#xff0c;可直接使用實驗臺上數字電路實驗區的D觸發器&#xff0c;74LS138為地址譯碼器&#xff0c; Y0&#xff1a;280H&#xff5e;287H&…

Linux 使用Nginx搭建簡易網站模塊

網站需求&#xff1a; 一、基于域名[www.openlab.com](http://www.openlab.com)可以訪問網站內容為 welcome to openlab ? 二、給該公司創建三個子界面分別顯示學生信息&#xff0c;教學資料和繳費網站&#xff0c;基于[www.openlab.com/student](http://www.openlab.com/stud…

MyBatis 如何使用

1. 環境準備 添加依賴&#xff08;Maven&#xff09; 在 pom.xml 中添加 MyBatis 和數據庫驅動依賴&#xff1a; <dependencies><!-- MyBatis 核心庫 --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId&g…

ArkTS組件的三個通用(通用事件、通用屬性、通用手勢)

文章目錄 通用事件點擊事件 onClick觸摸事件 onTouch掛載、卸載事件拖拽事件按鍵事件 onKeyEvent焦點事件鼠標事件懸浮事件組件區域變化事件 onAreaChange組件尺寸變化事件組件可見區域變化事件組件快捷鍵事件自定義事件分發自定義事件攔截 通用屬性尺寸設置位置設置布局約束邊…

智慧城市像一張無形大網,如何緊密連接你我他?

智慧城市作為復雜巨系統&#xff0c;其核心在于通過技術創新構建無縫連接的網絡&#xff0c;使物理空間與數字空間深度融合。這張"無形大網"由物聯網感知層、城市數據中臺、人工智能中樞、數字服務入口和安全信任機制五大支柱編織而成&#xff0c;正在重塑城市運行規…

【python】django sqlite版本過低怎么辦

方法一&#xff1a;下載最新版本 復制上面的內容的鏈接 在服務器上進行操作 wget https://sqlite.org/2025/sqlite-autoconf-3490100.tar.gz tar -zxvf sqlite-autoconf-3490100.tar.gz cd sqlite-autoconf-3490100 ./configure --prefix/usr/local make && make in…

PyTorch - Tensor 學習筆記

上層鏈接&#xff1a;PyTorch 學習筆記-CSDN博客 Tensor 初始化Tensor import torch import numpy as np# 1、直接從數據創建張量。數據類型是自動推斷的 data [[1, 2],[3, 4]] x_data torch.tensor(data)torch.tensor([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])輸出&am…

【技術派后端篇】ElasticSearch 實戰指南:環境搭建、API 操作與集成實踐

1 ES介紹及基本概念 ElasticSearch是一個基于Lucene 的分布式、高擴展、高實時的基于RESTful 風格API的搜索與數據分析引擎。 RESTful 風格API的特點&#xff1a; 接受HTTP協議的請求&#xff0c;返回HTTP響應&#xff1b;請求的參數是JSON&#xff0c;返回響應的內容也是JSON…

從標準九九表打印解讀單行表達式的書寫修煉(Python)

解讀單行表達式書寫&#xff0c;了解修習單行捷徑。 筆記模板由python腳本于2025-04-16 23:24:17創建&#xff0c;本篇筆記適合喜歡單行喜好python的coder翻閱。 【學習的細節是歡悅的歷程】 博客的核心價值&#xff1a;在于輸出思考與經驗&#xff0c;而不僅僅是知識的簡單復述…

深入解析布爾注入:原理、實戰與防御

目錄 一、布爾注入的原理與核心邏輯 二、布爾注入的實戰步驟 三、關鍵函數與繞過技巧 四、實戰案例&#xff1a;獲取數據庫名稱 五、防御策略與最佳實踐 六、總結 一、布爾注入的原理與核心邏輯 布爾注入&#xff08;Boolean-Based Blind SQL Injection&#xff09;是一種…

OpenGL學習筆記(幾何著色器、實例化、抗鋸齒)

目錄 幾何著色器爆破物體法向量可視化 實例化&#xff08;偏移量存在uniform中&#xff09;實例化數組&#xff08;偏移量存在頂點屬性中&#xff09;小行星帶 抗鋸齒SSAA&#xff08;Super Sample Anti-aliasing&#xff09;MSAA&#xff08;Multi-Sampling Anti-aliasing&…

idea報錯java: 非法字符: ‘\ufeff‘解決方案

解決方案步驟以及說明 BOM是什么&#xff1f;1. BOM的作用2. 為什么會出現 \ufeff 錯誤&#xff1f;3. 如何解決 \ufeff 問題&#xff1f; 最后重新編譯&#xff0c;即可運行&#xff01;&#xff01;&#xff01; BOM是什么&#xff1f; \ufeff 是 Unicode 中的 BOM&#xff0…