【c++复习笔记】——智能指针详细解析(智能指针的使用,原理分析)
- 💂 個(gè)人主頁:努力學(xué)習(xí)的少年
- 🤟 版權(quán):?本文由【努力學(xué)習(xí)的少年】原創(chuàng)、在CSDN首發(fā)、需要轉(zhuǎn)載請(qǐng)聯(lián)系博主
- 💬 如果文章對(duì)你有幫助、歡迎關(guān)注、點(diǎn)贊、收藏(一鍵三連)和訂閱專欄哦
目錄
一. 智能指針的基本概念
二.? 智能指針的定義和使用
三. auto_ptr
四. unique_ptr
5. 定制刪除器
一. 智能指針的基本概念
1. RAll
RAII(Resource Acquisition Is Initialization)是一種利用對(duì)象生命周期來控制程序資源(如內(nèi)存、文件句柄、網(wǎng)絡(luò)連接、互斥量等等)的簡(jiǎn)單技術(shù)。
在對(duì)象構(gòu)造時(shí)獲取資源,接著控制對(duì)資源的訪問使之在對(duì)象的生命周期內(nèi)始終保持有效,最后在對(duì)象析構(gòu)的時(shí)候釋放資源。借此,我們實(shí)際上把管理一份資源的責(zé)任托管給了一個(gè)對(duì)象。這種做法有兩大好處:
- 不需要顯式地釋放資源。?
- 采用這種方式,對(duì)象所需的資源在其生命期內(nèi)始終保持有效。
2.智能指針概念
??? 在c++中,動(dòng)態(tài)內(nèi)存的管理式通過一對(duì)運(yùn)算符來完成的:new,在動(dòng)態(tài)內(nèi)存中為對(duì)象分配空間并返回一個(gè)指向該對(duì)象的指針,我們可以選擇對(duì)對(duì)象進(jìn)行初始化;delete,接受一個(gè)動(dòng)態(tài)對(duì)象的指針,銷毀該對(duì)象,并釋放與之關(guān)聯(lián)的內(nèi)存。動(dòng)態(tài)內(nèi)存的使用很容易出現(xiàn)問題,因?yàn)榇_保在正確的時(shí)間釋放內(nèi)存是極其困難的。有時(shí)使用完對(duì)象后,忘記釋放內(nèi)存,造成內(nèi)存泄漏的問題。
- ? 所謂的智能指針本質(zhì)就是一個(gè)類模板,它可以創(chuàng)建任意的類型的指針對(duì)象,當(dāng)智能指針對(duì)象使用完后,對(duì)象就會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù)去釋放該指針?biāo)赶虻目臻g。
下面是智能指針的基本框架,所有的智能指針類模板中都需要包含一個(gè)指針對(duì)象,構(gòu)造函數(shù)和析構(gòu)函數(shù)。
二.? 智能指針的定義和使用
- 智能指針的使用跟普通指針類似,可以使用運(yùn)算符“ * " 和 ” -> "去獲得指向的對(duì)象,因此,我們就需要在類中重載" * " 和" -> "函數(shù)。
- 當(dāng)程序結(jié)束時(shí),此時(shí)ptr1和ptr2指針被銷毀時(shí),對(duì)象ptr1和ptr2會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù)去釋放所指向的資源,這是智能指針特點(diǎn)。
- ?由于我的類中沒有定義拷貝構(gòu)造函數(shù)和賦值重載函數(shù),那么我們只能調(diào)用類中原生的拷貝構(gòu)造函數(shù)和賦值重載函數(shù)。那么就會(huì)程序就會(huì)出現(xiàn)崩潰的問題,如下:
- ptr2和ptr1指向的同一塊空間,當(dāng)ptr2被銷毀時(shí),它會(huì)調(diào)用它的析構(gòu)函數(shù)去delete該資源對(duì)象,當(dāng)ptr1被銷毀時(shí),也會(huì)去調(diào)用它的析構(gòu)函數(shù)去釋放ptr1所指向的資源.所以,當(dāng)程序結(jié)束時(shí),ptr2被先被銷毀,同時(shí)釋放ptr2所指向的資源,然后ptr1被銷毀,也去釋放該資源對(duì)象,那么如下的資源對(duì)象同時(shí)被釋放兩次,所以程序就會(huì)被崩潰掉。(資源對(duì)象被釋放后,如果再去釋放該資源,程序就會(huì)崩潰)
?綜上所述,我們不能使用原生的拷貝構(gòu)造函數(shù)和賦值重載函數(shù),并且定義的拷貝構(gòu)造函數(shù)和賦值重載函數(shù)需要考慮只能釋放一次資源對(duì)象。
c++庫中的智能指針
三. auto_ptr
auto_ptr是c++98版本庫中提供的智能指針,該指針解決上訴的問題采取的措施是管理權(quán)轉(zhuǎn)移的思想,也就是原對(duì)象拷貝給新對(duì)象的時(shí)候,原對(duì)象就會(huì)被設(shè)置為nullptr,此時(shí)就只有新對(duì)象指向一塊資源空間。
?如果auto_ptr調(diào)用拷貝構(gòu)造函數(shù)或者賦值重載函數(shù)后,如果再去使用原來的對(duì)象的話,那么整個(gè)程序就會(huì)崩潰掉(因?yàn)樵瓉淼膶?duì)象被設(shè)置為nullptr),這對(duì)程序是有很大的傷害的.所以很多公司會(huì)禁用auto_ptr智能指針。
auto_ptr的拷貝構(gòu)造函數(shù)和賦值重載函數(shù)的實(shí)現(xiàn)
四. unique_ptr
unique_ptr是c++11版本庫中提供的智能指針,它直接將拷貝構(gòu)造函數(shù)和賦值重載函數(shù)給禁用掉,因此,不讓其進(jìn)行拷貝和賦值。
unique_ptr的拷貝函數(shù)和賦值重載函數(shù)
五. share_ptr
1. shared_ptr的基本概念
share_ptr是c++11版本庫中的智能指針,shared_ptr允許多個(gè)智能指針可以指向同一塊資源,并且能夠保證共享的資源只會(huì)被釋放一次,因此是程序不會(huì)崩潰掉。
2. shared_ptr的原理
shared_ptr采用的是引用計(jì)數(shù)原理來實(shí)現(xiàn)多個(gè)shared_ptr對(duì)象之間共享資源:
- shared_ptr在內(nèi)部會(huì)維護(hù)著一份引用計(jì)數(shù),用來記錄該份資源被幾個(gè)對(duì)象共享。
- 當(dāng)一個(gè)shared_ptr對(duì)象被銷毀時(shí)(調(diào)用析構(gòu)函數(shù)),析構(gòu)函數(shù)內(nèi)就會(huì)將該計(jì)數(shù)減1。
- 如果引用計(jì)數(shù)減為0后,則表示自己是最后一個(gè)使用該資源的shared_ptr對(duì)象,必須釋放資源。
- 如果引用計(jì)數(shù)不是0,就說明自己還有其他對(duì)象在使用,則不能釋放該資源,否則其他對(duì)象就成為野指針。
引用計(jì)數(shù)是用來記錄資源對(duì)象中有多少個(gè)指針指向該資源對(duì)象。
?
?銷毀過程:
3. shared_ptrd的實(shí)現(xiàn)
賦值重載的三種情況:
- ptr1=ptr1;智能指針自己給自己賦值,不做處理
- ptr2=ptr1;如果ptr1和ptr2指向同一塊空間,不做處理
- ptr2=ptr1;如果ptr2和ptr1指向的空間不一樣,處理過程如下:
- ?因?yàn)?span style="color:#4da8ee;">_ptrcount指向的對(duì)象是在堆上,因此所有的線程都能夠訪問到該資源,多線程在修改_ptrcount時(shí),則會(huì)出現(xiàn)線程安全問題,因此需要在修改_prtcount時(shí)需要用鎖來保證其數(shù)據(jù)的正確性。
- “? * "會(huì)返回ptr指向的對(duì)象,為什么不需要鎖對(duì)其進(jìn)行保護(hù)?因?yàn)閜tr返回的對(duì)象有可能被讀或者被寫,這個(gè)不是指針內(nèi)部所考慮的,而是由調(diào)用者進(jìn)行考慮的。
4. shared_ptr的循環(huán)引用
shared_ptr固然好用,但是它也會(huì)有問題存在。假設(shè)我們要使用定義一個(gè)雙向鏈表,如果我們想要讓創(chuàng)建出來的鏈表的節(jié)點(diǎn)都定義成shared_ptr智能指針,那么也需要將節(jié)點(diǎn)內(nèi)的_pre和_next都定義成shared_ptr的智能指針。如果定義成普通指針,那么就不能賦值給shared_ptr的智能指針。
當(dāng)其中兩個(gè)節(jié)點(diǎn)互相引用的時(shí)候,就會(huì)出現(xiàn)循環(huán)引用的現(xiàn)象。如下:
- ?use_count(): 返回智能指針對(duì)象的引用計(jì)數(shù)。
- 當(dāng)創(chuàng)建出node1和node2智能指針對(duì)象時(shí),引用計(jì)數(shù)都是1.
- 當(dāng)node1的next指向node2所指向的資源時(shí),node2的引用計(jì)數(shù)就+1,變成2,node2的pre指向noede1所指向的資源時(shí),node1的引用計(jì)數(shù)+1,變成2.
- 當(dāng)這兩個(gè)智能指針使用完后,調(diào)用析構(gòu)函數(shù),引用計(jì)數(shù)都-1,都變成1,由于引用計(jì)數(shù)不為0,所以node1和node2所指向的對(duì)象不會(huì)被釋放。
- 當(dāng)node1所指向的資源釋放需要當(dāng)node2中的_prev被銷毀,就需要node2資源的釋放,node2所指向的資源釋放就需要當(dāng)node1中的_next被銷毀,就需要node1資源的釋放。因此node1和node2都有對(duì)方的“把柄”,這兩個(gè)就造成循環(huán)引用現(xiàn)象,最終這node1和node2資源就不會(huì)進(jìn)行釋放。
那么如何解決這個(gè)shared_ptr的循環(huán)引用呢?
- c++庫中存在weak_ptr類型的智能指針。weak_ptr類的對(duì)象它可以指向shared_ptr,并且不會(huì)改變shared_ptr的引用計(jì)數(shù)。一旦最后一個(gè)shared_ptr被銷毀時(shí),對(duì)象就會(huì)被釋放。
weak_ptr對(duì)象指向shared_ptr對(duì)象時(shí),不會(huì)增加shared_ptr中的引用計(jì)數(shù),因此當(dāng)node1銷毀掉時(shí),則node1指向的空間就會(huì)被銷毀掉,node2類似,所以weak_ptr指針可以很好解決循環(huán)引用的問題。
- ?所以在定義雙向鏈表或者在二叉樹等有多個(gè)指針的時(shí)候,如果想要將該類型定義成智能指針,那么結(jié)構(gòu)體內(nèi)的指針需要定義成weak_ptr類型的指針,防止循環(huán)引用的出現(xiàn)。
weak_ptr簡(jiǎn)單實(shí)現(xiàn)
5. 定制刪除器
??? 當(dāng)我們釋放一個(gè)指向數(shù)組的指針的時(shí)候,delete[]后面的空方括號(hào)是必須存在(如下),它指示編譯器此指針指向的是一個(gè)對(duì)象數(shù)組的第一個(gè)元素,如果我們?cè)赿elete一個(gè)指向數(shù)組的指針中忽略了方括號(hào),我們的程序可能在執(zhí)行過程中在沒有任何警告下行為異常。
- ?我們?nèi)绻趧?dòng)態(tài)內(nèi)存中創(chuàng)建出一個(gè)數(shù)組,用一個(gè)shared_ptr對(duì)象去指向該數(shù)組,當(dāng)shared_ptr使用完后,就會(huì)去調(diào)用析構(gòu)函數(shù),由于shared_ptr默認(rèn)的刪除方式是 delete ptr,后面沒有帶方括號(hào),那么程序就會(huì)崩掉
- ?如果我們打開一個(gè)了文件,返回一個(gè)文件指針,讓一個(gè)shared_ptr對(duì)象去指向該文件,那么在調(diào)用析構(gòu)函數(shù)的時(shí)候就不能采用delete方法,而是使用flose()函數(shù)去關(guān)閉該文件。
因此,shared_ptr 類中提供了一個(gè)構(gòu)造函數(shù)可以自定義一個(gè)刪除器去指定析構(gòu)函數(shù)的刪除方式。
?這個(gè)自定義刪除器可以是函數(shù)指針,仿函數(shù),lamber,包裝器。
仿函數(shù)的刪除器
shared_ptr中的析構(gòu)函數(shù)會(huì)去調(diào)用DelArry仿函數(shù)去釋放動(dòng)態(tài)數(shù)組。
總結(jié)
以上是生活随笔為你收集整理的【c++复习笔记】——智能指针详细解析(智能指针的使用,原理分析)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++——智能指针
- 下一篇: 非接触式IC卡、条码卡、磁道卡、接触式I