智能指针详细解析(智能指针的使用,原理解析)
本文轉自努力的少年博主:
 https://blog.csdn.net/sjp11/article/details/123899141
目錄
一. 智能指針的基本概念
二.? 智能指針的定義和使用
三. auto_ptr
四. unique_ptr
五. share_ptr
1. shared_ptr的基本概念
2. shared_ptr的原理
3. shared_ptrd的實現
4. shared_ptr的循環引用
5. 定制刪除器
一. 智能指針的基本概念
1. RAll
RAII(Resource Acquisition Is Initialization)是一種利用對象生命周期來控制程序資源(如內存、文件句柄、網絡連接、互斥量等等)的簡單技術。
 在對象構造時獲取資源,接著控制對資源的訪問使之在對象的生命周期內始終保持有效,最后在對象析構的時候釋放資源。借此,我們實際上把管理一份資源的責任托管給了一個對象。這種做法有兩大好處:
- 不需要顯式地釋放資源。?
- 采用這種方式,對象所需的資源在其生命期內始終保持有效。
2.智能指針概念
??? 在c++中,動態內存的管理式通過一對運算符來完成的:new,在動態內存中為對象分配空間并返回一個指向該對象的指針,我們可以選擇對對象進行初始化;delete,接受一個動態對象的指針,銷毀該對象,并釋放與之關聯的內存。動態內存的使用很容易出現問題,因為確保在正確的時間釋放內存是極其困難的。有時使用完對象后,忘記釋放內存,造成內存泄漏的問題。
- ? 所謂的智能指針本質就是一個類模板,它可以創建任意的類型的指針對象,當智能指針對象使用完后,對象就會自動調用析構函數去釋放該指針所指向的空間。
下面是智能指針的基本框架,所有的智能指針類模板中都需要包含一個指針對象,構造函數和析構函數。
二.? 智能指針的定義和使用
- 智能指針的使用跟普通指針類似,可以使用運算符“ * " 和 ” -> "去獲得指向的對象,因此,我們就需要在類中重載" * " 和" -> "函數。
- 當程序結束時,此時ptr1和ptr2指針被銷毀時,對象ptr1和ptr2會自動調用析構函數去釋放所指向的資源,這是智能指針特點。
- ?由于我的類中沒有定義拷貝構造函數和賦值重載函數,那么我們只能調用類中原生的拷貝構造函數和賦值重載函數。那么就會程序就會出現崩潰的問題,如下:
- ptr2和ptr1指向的同一塊空間,當ptr2被銷毀時,它會調用它的析構函數去delete該資源對象,當ptr1被銷毀時,也會去調用它的析構函數去釋放ptr1所指向的資源.所以,當程序結束時,ptr2被先被銷毀,同時釋放ptr2所指向的資源,然后ptr1被銷毀,也去釋放該資源對象,那么如下的資源對象同時被釋放兩次,所以程序就會被崩潰掉。(資源對象被釋放后,如果再去釋放該資源,程序就會崩潰)
?綜上所述,我們不能使用原生的拷貝構造函數和賦值重載函數,并且定義的拷貝構造函數和賦值重載函數需要考慮只能釋放一次資源對象。
c++庫中的智能指針
三. auto_ptr
auto_ptr是c++98版本庫中提供的智能指針,該指針解決上訴的問題采取的措施是管理權轉移的思想,也就是原對象拷貝給新對象的時候,原對象就會被設置為nullptr,此時就只有新對象指向一塊資源空間。
?如果auto_ptr調用拷貝構造函數或者賦值重載函數后,如果再去使用原來的對象的話,那么整個程序就會崩潰掉(因為原來的對象被設置為nullptr),這對程序是有很大的傷害的.所以很多公司會禁用auto_ptr智能指針。
auto_ptr的拷貝構造函數和賦值重載函數的實現
四. unique_ptr
unique_ptr是c++11版本庫中提供的智能指針,它直接將拷貝構造函數和賦值重載函數給禁用掉,因此,不讓其進行拷貝和賦值。
unique_ptr的拷貝函數和賦值重載函數
五. share_ptr
1. shared_ptr的基本概念
share_ptr是c++11版本庫中的智能指針,shared_ptr允許多個智能指針可以指向同一塊資源,并且能夠保證共享的資源只會被釋放一次,因此是程序不會崩潰掉。
2. shared_ptr的原理
shared_ptr采用的是引用計數原理來實現多個shared_ptr對象之間共享資源:
- shared_ptr在內部會維護著一份引用計數,用來記錄該份資源被幾個對象共享。
- 當一個shared_ptr對象被銷毀時(調用析構函數),析構函數內就會將該計數減1。
- 如果引用計數減為0后,則表示自己是最后一個使用該資源的shared_ptr對象,必須釋放資源。
- 如果引用計數不是0,就說明自己還有其他對象在使用,則不能釋放該資源,否則其他對象就成為野指針。
引用計數是用來記錄資源對象中有多少個指針指向該資源對象。
?
?銷毀過程:
3. shared_ptrd的實現
賦值重載的三種情況:
- ptr1=ptr1;智能指針自己給自己賦值,不做處理
- ptr2=ptr1;如果ptr1和ptr2指向同一塊空間,不做處理
- ptr2=ptr1;如果ptr2和ptr1指向的空間不一樣,處理過程如下:
- ?因為_ptrcount指向的對象是在堆上,因此所有的線程都能夠訪問到該資源,多線程在修改_ptrcount時,則會出現線程安全問題,因此需要在修改_prtcount時需要用鎖來保證其數據的正確性。
- “? * "會返回ptr指向的對象,為什么不需要鎖對其進行保護?因為ptr返回的對象有可能被讀或者被寫,這個不是指針內部所考慮的,而是由調用者進行考慮的。
4. shared_ptr的循環引用
shared_ptr固然好用,但是它也會有問題存在。假設我們要使用定義一個雙向鏈表,如果我們想要讓創建出來的鏈表的節點都定義成shared_ptr智能指針,那么也需要將節點內的_pre和_next都定義成shared_ptr的智能指針。如果定義成普通指針,那么就不能賦值給shared_ptr的智能指針。
當其中兩個節點互相引用的時候,就會出現循環引用的現象。如下:
- ?use_count(): 返回智能指針對象的引用計數。
- 當創建出node1和node2智能指針對象時,引用計數都是1.
- 當node1的next指向node2所指向的資源時,node2的引用計數就+1,變成2,node2的pre指向noede1所指向的資源時,node1的引用計數+1,變成2.
- 當這兩個智能指針使用完后,調用析構函數,引用計數都-1,都變成1,由于引用計數不為0,所以node1和node2所指向的對象不會被釋放。
- 當node1所指向的資源釋放需要當node2中的_prev被銷毀,就需要node2資源的釋放,node2所指向的資源釋放就需要當node1中的_next被銷毀,就需要node1資源的釋放。因此node1和node2都有對方的“把柄”,這兩個就造成循環引用現象,最終這node1和node2資源就不會進行釋放。
那么如何解決這個shared_ptr的循環引用呢?
- c++庫中存在weak_ptr類型的智能指針。weak_ptr類的對象它可以指向shared_ptr,并且不會改變shared_ptr的引用計數。一旦最后一個shared_ptr被銷毀時,對象就會被釋放。
weak_ptr對象指向shared_ptr對象時,不會增加shared_ptr中的引用計數,因此當node1銷毀掉時,則node1指向的空間就會被銷毀掉,node2類似,所以weak_ptr指針可以很好解決循環引用的問題。
- ?所以在定義雙向鏈表或者在二叉樹等有多個指針的時候,如果想要將該類型定義成智能指針,那么結構體內的指針需要定義成weak_ptr類型的指針,防止循環引用的出現。
weak_ptr簡單實現
5. 定制刪除器
??? 當我們釋放一個指向數組的指針的時候,delete[]后面的空方括號是必須存在(如下),它指示編譯器此指針指向的是一個對象數組的第一個元素,如果我們在delete一個指向數組的指針中忽略了方括號,我們的程序可能在執行過程中在沒有任何警告下行為異常。
- ?我們如果在動態內存中創建出一個數組,用一個shared_ptr對象去指向該數組,當shared_ptr使用完后,就會去調用析構函數,由于shared_ptr默認的刪除方式是 delete ptr,后面沒有帶方括號,那么程序就會崩掉
- ?如果我們打開一個了文件,返回一個文件指針,讓一個shared_ptr對象去指向該文件,那么在調用析構函數的時候就不能采用delete方法,而是使用flose()函數去關閉該文件。
因此,shared_ptr 類中提供了一個構造函數可以自定義一個刪除器去指定析構函數的刪除方式。
?這個自定義刪除器可以是函數指針,仿函數,lamber,包裝器。
仿函數的刪除器
shared_ptr中的析構函數會去調用DelArry仿函數去釋放動態數組。
總結
以上是生活随笔為你收集整理的智能指针详细解析(智能指针的使用,原理解析)的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 大学计算机案例实验教程文件,大学计算机实
- 下一篇: 魔改 Typora 主题
