智能指針之設計模式4

前面的文章介紹了使用工廠模式來封裝智能指針對象的創建過程,下面介紹一下工廠類

enable_shared_from_this的實現方案。

4、模板方法模式

在前面的文章分析過,enable_shared_from_this<T>類是一個工廠基類,提供的工廠方法是shared_from_this(),而shared_ptr<T>就是一個具體的產品類,每一個shared_ptr<T>對象都需要一個具體的工廠類來創建它,這個具體的工廠類就是T,它是enable_shared_from_this<T>的派生類,同時又是模板參數類型。那么,C++標準庫是怎么通過this指針來創建shared_ptr<T>對象的呢?

按照常規的做法,可以繼承工廠基類然后由派生類來實現基類定義的工廠方法接口,然而當在一個對象內部通過this指針創建一個shared_ptr對象時,卻不是一件容易的事情:

首先,要判斷資源對象是否是在堆中,如果資源對象不是在堆中,那么使用shared_ptr<T>(this)創建的智能指針對象,在析構時就會發生異常。

其次,要確定資源對象是否已經被shared_ptr管理了,如果管理了,也不能直接使用shared_ptr<T>(this)創建的智能指針對象,否則會導致由兩個不同的shared_ptr來管理this,最后會發生重復釋放內存的錯誤。

再次,如果在資源對象內部使用shared_ptr<T>(this)創建了智能指針對象,但是用戶在外部是不知道的,可能又使用shared_ptr<T>(obj_ptr)創建了智能指針對象,也會發生重復釋放內存的錯誤。

如果這個創建過程,完全交給程序員來實現,需要仔細地約束各方面的代碼實現,還得需要相關人員認真地評審,防止出現上面描述的錯誤,帶來了極大的風險和不便。C++標準庫的思路是交由代碼來解決,把通過this指針創建shared_ptr對象的代碼封裝起來形成固定的模板套路,只把需要定制的部分交給派生類去實現,也就是模板方法模式,即在模板基類中把通過this指針創建shared_ptr對象的過程封裝成“模板方法”,它執行過程中要使用具體派生類提供的“鉤子函數",這樣哪個類要想提供shared_from_this()功能,就繼承模板基類并實現”鉤子函數“就可以了。下面是GOF模板方法模式的結構圖:
在這里插入圖片描述

從結構圖中可以看出,模板方法模式實際上就是面向對象編程中的繼承和動態多態機制,也就是在基類中定義virtual函數接口,也就是“鉤子方法”,在派生類中實現virtual函數,從而讓基類中的“模板方法”能夠通過動態綁定的方式,來調用派生類提供的virtual函數。

不過enable_shared_from_this<T>作為模板基類與上面的模板方法模式在形式上不同,它沒有采用面向對象編程的多態機制,而是利使用了一種特殊的繼承方式實現的,即CRTP。CRTP采用了C++的模板技術,雖然也是繼承方式,但卻是在基類中通過把自己強制轉為派生類類型的方式來調用派生類實現的函數,這是二者最大的區別。模板方法模式是一個共有的基類,然后有多個同一族類的派生類,它們復用了基類中的“模板方法”,并重寫了virtual函數,屬于動態綁定;而CRTP方式是一個基類僅有一個派生類,這樣完全可以把基類強制轉換成派生類,調用的是派生類的非virtual成員函數,屬于靜態綁定機制。同樣,因為CRTP派生類繼承了基類,它復用了基類的“模板方法”,因此,外界也可以調用派生類的“模板方法”來實現具體的功能。

shared_from_this()創建shared_ptr對象的套路是,定義了一個weak_ptr數據成員,在創建shared_ptr對象時,就使用這個weak_ptr成員來創建,派生類顯然是無法設置這個weak_ptr成員的,也就是說無法設計成提供一個“鉤子函數”的形式,而weak_ptr類型和shared_ptr類型息息相關,因此enable_shared_from_this<T>就讓shared_ptr<T>作為它的友元類,要求shared_ptr在創建對象時,同時初始化這個weak_ptr成員。這樣,如果派生類T沒有使用shared_ptr對象來管理自己的生存期,也就初始化不了這個weak_ptr成員,進而也就無法通過this創建出shared_ptr對象,而通過weak_ptr對象創建的shared_ptr對象和原來的shared_ptr對象共享T資源對象的所有權,也不會發生直接使用this指針創建了shared_ptr對象之后,導致由兩個不同的shared_ptr來管理this的錯誤。

可見,在這個模板方法模式中,“鉤子方法”不是一個具體函數,它是一個抽象的概念,或者說是一個“鉤子數據”,在這里就是它的weak_ptr類型的數據成員,模板方法在實現套路化的邏輯時,需要使用weak_ptr對象數據成員來創建shared_ptr對象,這個weak_ptr對象是在創建shared_ptr對象時初始化的,因此要求創建它的派生類對象時,必須要被一個shared_ptr對象托管,這樣shared_ptr在創建對象時,就會同時初始化這個weak_ptr成員。也就是說,派生類不需要提供具體的“鉤子函數”,只要它在創建時候,同時創建一個shared_ptr對象來管理它的生存期,它自然而然地就為基類的“模板方法”提供了“鉤子數據”。

這里,相當于把通過this指針創建shared_ptr對象的過程給封裝成模板方法,誰要是想通過this指針創建shared_ptr對象就可以直接繼承enable_shared_from_this類,它的模板方法自己實現了這個功能,派生類無需專門編寫代碼,為程序員編程帶來了便利。下面是結構圖:
在這里插入圖片描述

enable_shared_from_this<resource>是工廠基類也是模板方法類,它的成員函數shared_from_this()是"模板方法",職責是使用資源對象resource的this指針來創建一個shared_ptr對象;資源對象resource類是它的一個公共派生類,是工廠派生類也是模板方法派生類,具體產品對象shared_ptr<resource>就是從resource中創建出來的,也就是一個產品對象通過“模板方法”創建了一個管理自己生存期的shared_ptr對象。示例代碼如下:

class resource : public enable_shared_from_this<resource> {data_obj obj;public:void process() {auto sp = shared_from_this();...}... // 其它成員函數
};
...

只要繼承了enable_shared_from_this<T>類,它就是一個模板方法派生類,就可以使用模板方法類的shared_from_this(),然而這只是“模板方法”函數,還得需要派生類自己提供“鉤子方法”函數,也就是保證派生類創建的對象被shared_ptr托管,這樣就可以根據模板方法來通過this指針創建shared_ptr的副本了。因此,resoure資源對象可以通過:

shared_ptr<resource> res1(new resource);
shared_ptr<resource> res2 = make_shared<resource>();
shared_ptr<resource> res3 = weak_ptr_obj.lock();
shared_ptr<resource> res4(weak_ptr_obj());
shared_ptr<resource> res5(unique_ptr_obj());

等這些方式來創建shared_ptr<resource>對象,可以把這些創建方式等同于“鉤子方法”,派生類resource只要能夠創建出shared_ptr<resource>對象,當在resource對象內部使用this指針調用“模板方法”shared_shared_this()時,就會按照固定的套路創建出一個shared_ptr<resource>對象。顯然這樣就簡化了根據this創建shared_ptr<resource>對象的過程,程序也不需要作太多的考慮,只要保證resource對象是使用shared_ptr智能指針管理的就可以了。

總之,工廠方法shared_from_this()是使用模板方法模式實現的,而模板模式又是使用CRTP慣用法來實現的。

參考:
https://cloud.tencent.com/developer/article/2362395

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

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

相關文章

【Nova UI】十、打造組件庫第一個組件-圖標組件(下):從.svg 到 SVG Vue 組件的高效蛻變?

序言 在組件庫開發的精彩旅程中&#x1f680;&#xff0c;我們已經成功打造并完善了圖標組件體系&#xff0c;賦予其強大的功能和豐富的表現力&#x1f389;。然而&#xff0c;隨著業務版圖的不斷擴張&#x1f310;&#xff0c;手動逐個編寫 SVG Vue 組件的傳統方式&#xff0…

Golang | 倒排索引

文章目錄 倒排索引的設計倒排索引v0版實現 倒排索引的設計 通用搜索引擎 v.s. 垂直搜索引擎&#xff1a; 通用搜索引擎&#xff1a;什么都可以搜索&#xff0c;更加智能化垂直搜索引擎&#xff1a;只能搜自家數據庫里面的內容&#xff0c;一般都帶著搜索條件&#xff0c;搜索一…

Windows 10 上運行 Ollama 時遇到 llama runner process has terminated: exit status 2

在 Windows 10 上運行 Ollama 時遇到 llama runner process has terminated: exit status 2 錯誤&#xff0c;可能是由多種原因引起的。以下是逐步解決方案&#xff1a; 1. 檢查 Ollama 服務狀態 按 Win R 輸入 services.msc&#xff0c;找到 Ollama 服務&#xff0c;確保其狀…

PCI 總線學習筆記(五)

PCI 總線學習系列&#xff0c;參考自 技術大牛博客&#xff1a; PCIe 掃盲系列博文連載目錄篇 書籍&#xff1a;王齊老師的《PCI Express 體系結構導讀》 下面的文章中加入了自己的一些理解和實際使用中遇到的一些場景&#xff0c;供日后查詢和回憶使用 PCI 總線定義了兩類配置…

Spring Cloud Alibaba VS Spring Cloud

??Spring Cloud Alibaba 與 Spring Cloud 組件對比? ??服務發現與注冊中心? 功能???Spring Cloud???Spring Cloud Alibaba?對比說明??核心組件?EurekaNacosNacos 支持動態配置管理、健康檢查更靈活&#xff0c;且提供 DNS 服務發現能力。????健康檢查??…

Java—— 常見API介紹 第五期

JDK8以后新增的時間相關類 Date類ZoneId&#xff1a;時區Instant&#xff1a;時間戳ZoneDateTime&#xff1a;帶時區的時間 日期格式化類 SimpleDateFormat DateTimeFormatter&#xff1a;用于時間的格式化和解析 日歷類 Calendar LocalDate&#xff1a;年、月、日LocalTime…

Java與Kotlin在Android開發中的全面對比分析

趨勢很重要 語言發展背景與現狀 Android操作系統自2008年正式發布以來&#xff0c;Java長期作為其主要的開發語言。這種選擇源于Java語言的跨平臺特性、成熟的生態系統以及廣泛開發者基礎。然而&#xff0c;隨著移動開發需求的快速演變&#xff0c;Java在Android開發中逐漸暴…

第一部分:git基本操作

目錄 1、git初識 1.1、存在的問題 1.2、版本控制器 1.3、git安裝 1.3.1、CentOS平臺 1.3.2、ubuntu平臺 2、git基本操作 2.1、創建倉庫 2.2、配置git 3、工作區、暫存區、版本庫 4、基本操作 4.1、場景一 4.2、場景二 4.3、修改文件 5、版本回退 6、撤銷修改 …

正則表達式與python使用

一、Python正則表達式基礎 1. 導入模塊 Python通過 re 模塊實現正則表達式功能&#xff0c;需先導入模塊&#xff1a; import re2. 核心語法 普通字符&#xff1a;直接匹配字面值&#xff08;如 a 匹配字符 a&#xff09;。元字符&#xff1a; \d&#xff1a;匹配數字&…

從FP32到BF16,再到混合精度的全景解析

筆者做過目標檢測模型、超分模型以及擴散生成模型。其中最常使用的是單精度FP32、半精度FP16、BF16。 雙精度"FP64"就不說了&#xff0c;不太會用到。 #1. 單精度、半精度和混合精度 單精度&#xff08;FP32&#xff09;、半精度&#xff08;FP16&#xff09;和混合…

Hot100方法及易錯點總結2

本文旨在記錄做hot100時遇到的問題及易錯點 五、234.回文鏈表141.環形鏈表 六、142. 環形鏈表II21.合并兩個有序鏈表2.兩數相加19.刪除鏈表的倒數第n個節點 七、24.兩兩交換鏈表中的節點25.K個一組翻轉鏈表(坑點很多&#xff0c;必須多做幾遍)138.隨機鏈表的復制148.排序鏈表 N…

不在同一個局域網的遠程桌面連接怎么設置?本地內網計算機讓其他網絡遠程訪問6種常用方法

遠程桌面是一種重要的技術&#xff0c;它允許用戶通過網絡遠程訪問和控制另一臺計算機的桌面界面。但是&#xff0c;當被控制端和控制端不在同一個局域網內時&#xff0c;就需要進行一些額外的配置。本文將詳細介紹在不同局域網下設置遠程桌面的步驟&#xff0c;以幫助讀者順利…

天機學堂day10作業,完善兌換優惠券功能

UserCouponServiceImpl /*** 兌換碼兌換優惠券* param code*/TransactionalOverridepublic void exchangeCoupon(String code) {//1、校驗code是否為空if (StringUtils.isBlank(code)) {throw new BadRequestException("非法參數&#xff01;");}//2、解析兌換碼&…

JAVA工程師面試題(七)

1、遞歸實現1,1,2,3,5,8,….第30個數是多少&#xff1f; public static int Foo(int i) { if (i < 0) return 0; else if(i > 0 && i < 2) return 1; else return Foo(i -1) Foo(i - 2); }…

Qt基礎009(HTTP編程和QJSON)

文章目錄 軟件開發網絡架構BS架構/CS架構 HTTP基本概念QT的HTTP編程JSON數據概述QT生成JSON數據QT解析JSON數據 軟件開發網絡架構 BS架構/CS架構 ? 在計算機網絡和軟件開發中&#xff0c;CS架構&#xff08;Client-Server Architecture&#xff0c;客戶端-服務器架構&#x…

高精度電流檢測革命:同軸分流器的創新應用與技術演進

一、精密測量原理與結構創新 基于電磁場分布重構技術的新型同軸分流器&#xff0c;突破了傳統電流測量的物理限制。該器件采用三維環形電阻矩陣結構&#xff0c;通過多層級導電環的精密排列&#xff0c;實現了電流路徑的渦流自補償。區別于常規分流器的平板式設計&#xff0c;其…

【使用層次序列構建二叉樹(數據結構C)】

使用層次序列構建二叉樹&#xff08;C語言實現&#xff09; 在數據結構學習過程中&#xff0c;二叉樹的構建方式通常有遞歸建樹&#xff08;前序/中序&#xff09;和層次建樹&#xff08;廣度優先&#xff09;兩種。本文將介紹一種基于輔助隊列實現的層次建樹方法&#xff0c;并…

設置Rocky Linux盒蓋不休眠的3個簡單步驟

在 Rocky linux&#xff08;和其他基于 RHEL 的發行版&#xff09;中&#xff0c;當你關閉筆記本電腦的蓋子時&#xff0c;默認行為通常是使系統休眠。如果你想更改這一行為&#xff0c;例如&#xff0c;使系統在關閉蓋子時只是鎖定&#xff0c;你可以按照以下步驟操作&#xf…

WPF的發展歷程

文章目錄 WPF的發展歷程引言起源與背景&#xff08;2001-2006&#xff09;從Avalon到WPF設計目標與創新理念 WPF核心技術特點與架構基礎架構與渲染模型關鍵技術特點MVVM架構模式 WPF在現代Windows開發中的地位與前景當前市場定位與其他微軟UI技術的關系未來發展前景 社區貢獻與…

【器件專題1——IGBT第1講】IGBT:電力電子領域的 “萬能開關”,如何撐起新能源時代?

一、IGBT 是什么&#xff1f;重新認識這個 “低調的電力心臟” 你可能沒聽過 IGBT&#xff0c;但一定用過它驅動的設備&#xff1a;家里的變頻空調、路上的電動汽車、屋頂的光伏逆變器&#xff0c;甚至高鐵和電網的核心部件里&#xff0c;都藏著這個 “電力電子開關的瑞士軍刀”…